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

Remove DocumentAncestors recursively for all SmartObjects in a PSD

Community Beginner ,
Jul 01, 2022 Jul 01, 2022

I am currently working on a script that can clean the DocumentAncestors in a .psd file. 

Since there are SmartObjects in the SmartObjects of the PSD, I am current trying to recursively iterate trough all layers, check if a layer is a SmartObject and if so, iterate into it and check if any further SmartObjects exist. If no more SmartObjects exist, the script starts to delete the DocumentAncestors recursively. 

 

The problems:

- it seems to iterate correctly through all the layers, but the removal not always seems to work, since there are many DocumentAncestor entries left in the PSD afterwards

- the procedure is also very heavy on the woking volumes. I am currently trying to remove the Ancestors of a 800MB PSD and the working volumes need >150GB of space

- after about 5-10 min, the variable which holds the current SmartObject becomes invalid

 

The code implements several different little scripts and parts of users found on here (JJMackKukurykus just to name a few)

 

#target photoshop
var count = 0;
const doneIDs = [];
var doc = app.activeDocument;
doc.suspendHistory("Delete Ancestors Metadata", "processSOLayers(doc)");

function processSOLayers(theParent) 
{
    $.writeln("Entering Parent: " + theParent.name );
    count++;
    $.writeln( "Depth: " + count);

    for (var m = 0; m <  theParent.layers.length; m++) 
    {
        var theLayer = theParent.layers[m];

        if (theLayer.typename === "ArtLayer") 
        {
            if (theLayer.kind === LayerKind.SMARTOBJECT) 
            {
                var theVisibility = theLayer.visible;
                app.activeDocument.activeLayer = theLayer;

                var layerID = getLayerId();
                if( layerIsPsObject(layerID) )
                {
                    $.writeln("Opening SmartObject: " + theLayer.name);
                    var smartObject = openSmartObject(theLayer);
                    if( smartObject.id !== theParent.id && !isAlreadyDone(smartObject.id))
                    {
                        processSOLayers(smartObject);
                        deleteDocAncestors(smartObject);
                        smartObject.close(SaveOptions.SAVECHANGES);
                        $.writeln("Saved SmartObject: " + theLayer.name + " ID: " + theLayer.id);
                    }
                    else
                    {
                        smartObject.close(SaveOptions.DONOTSAVECHANGES);
                    }
                }
                theLayer.visible = theVisibility;
            }
            //alert("SO doc cleaned");
        } 
        else
        { 
            //Layerset
            processSOLayers(theLayer);
            $.writeln("Exiting LayerSet: " + theLayer.name );
        }
    }
    
    $.writeln("Exiting parent: " + theParent.name );
    count--;
    $.writeln( "Depth: " + count);
    return;
}

deleteDocAncestors(doc);
//alert("Parent doc cleaned");

function deleteDocAncestors(document) 
{
    if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
    var xmp = new XMPMeta(document.xmpMetadata.rawData);
    xmp.deleteProperty(XMPConst.NS_PHOTOSHOP, "DocumentAncestors");
    document.xmpMetadata.rawData = xmp.serialize();
    $.writeln("Cleaned Ancestors...")
}

function isAlreadyDone(id)
{
    for( var i = 0; i < doneIDs.length; ++i)
    {
        if( doneIDs[i] === id)
            return true;
    }

    doneIDs.push(id);
    return false;
}

function getLayerId()
{
    var ref = new ActionReference();
    ref.putEnumerated(charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt')); // reference is active layer
    var layerDesc = executeActionGet(ref);
    var layerID = layerDesc.getInteger(stringIDToTypeID('layerID'));

    return layerID;
}

function openSmartObject (theLayer) 
{
    try
    {
        var idplacedLayerEditContents = stringIDToTypeID( "placedLayerEditContents" );
        var desc2 = new ActionDescriptor();
        executeAction( idplacedLayerEditContents, desc2, DialogModes.NO );

        return app.activeDocument;
    }
    catch(e)
    {
        alert(e.name + " " + e.message);
        return undefined;
    }
};

function smartobject_file_ext(id) 
{  
    try 
    {         
        var r = new ActionReference();     
        r.putProperty(stringIDToTypeID("property"), stringIDToTypeID("smartObject"));  
        r.putIdentifier(stringIDToTypeID("layer"), id);  
        var name = executeActionGet(r).getObjectValue(stringIDToTypeID("smartObject")).getString(stringIDToTypeID("fileReference"));         
         
        var n = name.lastIndexOf(".");
        if (n < 0) return "";
     
        return name.substr(n+1).toLowerCase();  
    }  
    catch (e) 
    {
        $.writeln(e);
        return "error"; 
    }  
}   

function layerIsPsObject(layerID) 
{
	var ext = smartobject_file_ext(layerID);  
	switch (ext)  
		{  
        case "psb":
        case "psd":
            return true;
  		default:  
			return false;
    }  
}

 

 

 

TOPICS
Actions and scripting , Windows
4.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 1 Correct answer

Community Beginner , Jul 07, 2022 Jul 07, 2022

This is my final script, which could reduce a 800MB PSD to about 380MB.

@Stephen Marsh 

 

 

/*

Hacked together by m3zli
04.07.2022

Interesting posts, which helped me write this script:
https://community.adobe.com/t5/photoshop/inflated-jpg-file-size-photoshop-document-ancestors-metadata/m-p/8055434
https://feedback.photoshop.com/photoshop_family/topics/rasterize_all_smart_objects_in_a_psd_file_to_shrink_down_its_size
https://community.adobe.com/t5/photoshop-ecosystem-discussions/remove-document
...
Translate
Adobe
Community Expert ,
Mar 26, 2024 Mar 26, 2024

@JuDrus wrote:

And what it does with linked files? Because 3 out of my 13 linked files disappeared completely from my computer... not from layers, but from my project "linked files" folder...

 

I ran a script and got this error:

2024-03-26 10-56-14.png

 

After clicking OK, this popped up:

2024-03-26 10-56-23.png

 

And after clicking this OK, I saw that there were red question marks on a couple of my layers, so I thought "oh... what? how did they linked off? ok I will relink them"... But when I tried to relink them, these files were no longer in my "link files" folder... I also checked Recycle Bin, but they were not there either...

2024-03-26 11-05-16.png

 

So what happened and why did my linked files disappeared out of this world?

I'm glad that I tested it on my fresh project and it was only simple balloon files that I will be able to re-download, but if this would happen on a more complex file that was created from scratch, I would jump out of a window...



If you read this entire topic, then you'll know as much as I do!

 

I haven't tested with linked files or TIFF.

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
Participant ,
Mar 26, 2024 Mar 26, 2024

@m3zli Maybe you as creator could comment on this?

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 26, 2024 Mar 26, 2024

Hey,

I'm currently not maintaining this script, as I had a usecase for this nearly 2 years ago, but am not the one using it. Just from guessing it could probably be a problem with a newer version, as this script was tested on a veeery old CS 5 version. Sorry this wasn't helpful at all, but as said, I have no current usecase for now and won't look into this until I might face the same problem.

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 ,
Apr 24, 2024 Apr 24, 2024

@JuDrus  I don't know how much programming knowledge you have, but I think I can explain to you how to fix the problem. As I own no valid Photoshop license right now, I am not able to rewrite and test it myself, sorry.

 

I am currently not fully hands on the topic, but here's what I  think happens:

- tiff/tif files seem to work different than psd or psb SmartObjects

- it looks like they don't create an overhead, like psd or psb files, when iterating(opening) into them (based on your screenshot)

- the tiff/tif file gets incorrectly removed, because that's the standard procedure for psd and psb files

- the tiff/tif file is now broken, because we deleted its content

 

maybe that helps, but I think I can do no more from here on (till I get a new PS license at least 😉 )

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 24, 2024 Apr 24, 2024

@JuDrus @m3zli 

 

I have just taken a look at the code... I think that the answer is simple (I have performed limited testing). There is a "garbage collection" step that was intended to work with temp files for embedded smart objects, however, if the script is run on a linked smart object then the garbage collection deletes the linked file without recovery.

 

A simple, quick fix is to disable the offending step, changing the following line:

 

removePSB(path);

 

To:

 

//removePSB(path);

 

Another option would be to test if the smart object is linked, and if so skip the garbage collection.

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 24, 2024 Apr 24, 2024

Here is an edited version with a conditional check for the smart object being embedded. I have performed limited testing and this appears to stop linked smart objects being deleted from disk:

 

/*

https://community.adobe.com/t5/photoshop-ecosystem-discussions/remove-documentancestors-recursively-for-all-smartobjects-in-a-psd/td-p/13043605

Remove DocumentAncestors recursively for all SmartObjects in a PSD.jsx

Hacked together by m3zli
04.07.2022

Edited by Stephen Marsh
25th April 2024 - Conditional check for embedded smart object added to avoid deleting links smart object files

Interesting posts, which helped me write this script:
https://community.adobe.com/t5/photoshop/inflated-jpg-file-size-photoshop-document-ancestors-metadata/m-p/8055434
https://feedback.photoshop.com/photoshop_family/topics/rasterize_all_smart_objects_in_a_psd_file_to_shrink_down_its_size
https://community.adobe.com/t5/photoshop-ecosystem-discussions/remove-documentancestors-recursively-for-all-smartobjects-in-a-psd/m-p/13047853#M655448 
https://prepression.blogspot.com/2017/06/metadata-bloat-photoshopdocumentancestors.html 
https://community.adobe.com/t5/framemaker-discussions/directory-manipulation-as-part-of-an-extend-script/m-p/7822479

Adobe Photoshop CC JavaScript Reference 2020

*/

#target photoshop
var count = 0;
var doc = app.activeDocument;
doc.suspendHistory("Delete Ancestors Metadata", "processSOLayers(doc)");

function processSOLayers(theParent) 
{
    $.writeln("Entering Parent: " + theParent.name );
    count++;
    $.writeln( "Depth: " + count);

    var soLayers = getSmartObjectLayers();
    
    for (var m = 0; m < soLayers.length; m++) 
    {
        var layerID = soLayers[m];
        selectById(layerID);

        if( layerIsPhotoshopObject(layerID) )
        {
            $.writeln("Opening SmartObject ID: " + layerID);
            var smartObject = openSmartObject();
            var path = smartObject.fullName;

            if( smartObject.id !== theParent.id)
            {
                processSOLayers(smartObject);
                deleteDocAncestors(smartObject);
                smartObject.close(SaveOptions.SAVECHANGES);
                $.writeln("Saved SmartObject ID: " + layerID);

                // Garbage collection step modified to avoid deleting linked smart objects!
                var ref = new ActionReference();
                ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
                var layerDesc = executeActionGet(ref);
                ref.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("smartObject"));
                ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
                var so = executeActionGet(ref).getObjectValue(stringIDToTypeID("smartObject"));
                var soMoreDesc = layerDesc.getObjectValue(stringIDToTypeID('smartObjectMore'));
                if (so.getBoolean(stringIDToTypeID("linked")) === false) {
                    removePSB(path);
                }
            }
        }
    }
    
    $.writeln("Exiting parent: " + theParent.name );
    count--;
    $.writeln( "Depth: " + count);
    return;
}

deleteDocAncestors(doc);
doc.close(SaveOptions.SAVECHANGES);

function selectById(id) 
{
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putIdentifier(charIDToTypeID('Lyr '), id);
    desc.putReference(charIDToTypeID('null'), ref);
    executeAction(charIDToTypeID('slct'), desc, DialogModes.NO);
}

function getSmartObjectLayers() 
{
    var ids = [];
    var layers, desc, type, id;

    try
    {
      activeDocument.backgroundLayer;
      layers = 0;
    }
    catch (e)
    {
      layers = 1;
    }

    while (true)
    {
      ref = new ActionReference();
      ref.putIndex(charIDToTypeID('Lyr '), layers);
      try
      {
        desc = executeActionGet(ref);
      }
      catch (err)
      {
        break;
      }
      type = desc.getInteger(stringIDToTypeID("layerKind"));
      id = desc.getInteger(stringIDToTypeID("layerID"));
      if (type == 5 ) ids.push(id);
      layers++;
    }
    return ids;

}

function deleteDocAncestors(document) 
{
    if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
    var xmp = new XMPMeta(document.xmpMetadata.rawData);
    xmp.deleteProperty(XMPConst.NS_PHOTOSHOP, "DocumentAncestors");
    document.xmpMetadata.rawData = xmp.serialize();
    $.writeln("Cleaned Ancestors...")
}

function openSmartObject () 
{
    try
    {
        var idplacedLayerEditContents = stringIDToTypeID( "placedLayerEditContents" );
        var desc2 = new ActionDescriptor();
        executeAction( idplacedLayerEditContents, desc2, DialogModes.NO );

        return app.activeDocument;
    }
    catch(e)
    {
        alert(e.name + " " + e.message);
        return undefined;
    }
};

function removePSB(path)
{
    var file = new File(path);
    if(file.exists)
    {
        file.remove();
        $.writeln("Removed file: " + path);
    }
    file.close();
}

function getSmartObjectFileExtension(id) 
{  
    try 
    {         
        var r = new ActionReference();     
        r.putProperty(stringIDToTypeID("property"), stringIDToTypeID("smartObject"));  
        r.putIdentifier(stringIDToTypeID("layer"), id);  
        var name = executeActionGet(r).getObjectValue(stringIDToTypeID("smartObject")).getString(stringIDToTypeID("fileReference"));         
         
        var n = name.lastIndexOf(".");
        if (n < 0) return "";
     
        return name.substr(n+1).toLowerCase();  
    }  
    catch (e) 
    {
        $.writeln(e);
        return "error"; 
    }  
}   

function layerIsPhotoshopObject(layerID) 
{
	var ext = getSmartObjectFileExtension(layerID);  
    $.writeln("Object with ID " + layerID + " Ext: " + ext);
	switch (ext)  
		{  
        case "psb":
        case "psd":
        case "png":

            return true;
  		default:  
			return false;
    }  
}

 

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
New Here ,
Jul 16, 2025 Jul 16, 2025

Hello, thank you for this amazing script, it works wonderful!!
Would you mind helping me edit this script to ignore Generative Fill layers? Currently for it to work i have to merge all generative fill layer :< It would be life saving for me!!

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 ,
Jul 16, 2025 Jul 16, 2025
LATEST
quote

Hello, thank you for this amazing script, it works wonderful!!
Would you mind helping me edit this script to ignore Generative Fill layers? Currently for it to work i have to merge all generative fill layer :< It would be life saving for me!!


By @Kuro Kei

 

I have not tested this update, please test on a copy, just in case... use at your own risk!  :]

 

Feedback is appreciated.

 

/*

https://community.adobe.com/t5/photoshop-ecosystem-discussions/remove-documentancestors-recursively-for-all-smartobjects-in-a-psd/td-p/13043605

Remove DocumentAncestors recursively for all SmartObjects in a PSD 16july2025.jsx

Hacked together by m3zli
04.07.2022

Interesting posts, which helped me write this script:
https://community.adobe.com/t5/photoshop/inflated-jpg-file-size-photoshop-document-ancestors-metadata/m-p/8055434
https://feedback.photoshop.com/photoshop_family/topics/rasterize_all_smart_objects_in_a_psd_file_to_shrink_down_its_size
https://community.adobe.com/t5/photoshop-ecosystem-discussions/remove-documentancestors-recursively-for-all-smartobjects-in-a-psd/m-p/13047853#M655448 
https://prepression.blogspot.com/2017/06/metadata-bloat-photoshopdocumentancestors.html 
https://community.adobe.com/t5/framemaker-discussions/directory-manipulation-as-part-of-an-extend-script/m-p/7822479

Adobe Photoshop CC JavaScript Reference 2020

Edited by Stephen Marsh

25th April 2024 - Conditional check for embedded smart object added to avoid deleting links smart object files

16th July 2025 - Conditional check for generative smart object added at request

*/

#target photoshop
var count = 0;
var doc = app.activeDocument;
doc.suspendHistory("Delete Ancestors Metadata", "processSOLayers(doc)");

function processSOLayers(theParent) {
    $.writeln("Entering Parent: " + theParent.name);
    count++;
    $.writeln("Depth: " + count);

    var soLayers = getSmartObjectLayers();

    for (var m = 0; m < soLayers.length; m++) {
        var layerID = soLayers[m];
        selectById(layerID);

        if (layerIsPhotoshopObject(layerID)) {
            $.writeln("Opening SmartObject ID: " + layerID);
            var smartObject = openSmartObject();
            var path = smartObject.fullName;

            if (smartObject.id !== theParent.id) {
                processSOLayers(smartObject);
                deleteDocAncestors(smartObject);
                smartObject.close(SaveOptions.SAVECHANGES);
                $.writeln("Saved SmartObject ID: " + layerID);

                // Garbage collection step modified to avoid deleting linked smart objects!
                var ref = new ActionReference();
                ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
                var layerDesc = executeActionGet(ref);
                ref.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("smartObject"));
                ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
                var so = executeActionGet(ref).getObjectValue(stringIDToTypeID("smartObject"));
                var soMoreDesc = layerDesc.getObjectValue(stringIDToTypeID('smartObjectMore'));
                if (so.getBoolean(stringIDToTypeID("linked")) === false) {
                    removePSB(path);
                }

                // Garbage collection step modifed to avoid processing generative smart object layers
                if (soMoreDesc.hasKey(stringIDToTypeID('generativeDocInfo')) === false) {
                    removePSB(path);
                }
            }
        }
    }

    $.writeln("Exiting parent: " + theParent.name);
    count--;
    $.writeln("Depth: " + count);
    return;
}

deleteDocAncestors(doc);
doc.close(SaveOptions.SAVECHANGES);

function selectById(id) {
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putIdentifier(charIDToTypeID('Lyr '), id);
    desc.putReference(charIDToTypeID('null'), ref);
    executeAction(charIDToTypeID('slct'), desc, DialogModes.NO);
}

function getSmartObjectLayers() {
    var ids = [];
    var layers, desc, type, id;

    try {
        activeDocument.backgroundLayer;
        layers = 0;
    }
    catch (e) {
        layers = 1;
    }

    while (true) {
        ref = new ActionReference();
        ref.putIndex(charIDToTypeID('Lyr '), layers);
        try {
            desc = executeActionGet(ref);
        }
        catch (err) {
            break;
        }
        type = desc.getInteger(stringIDToTypeID("layerKind"));
        id = desc.getInteger(stringIDToTypeID("layerID"));
        if (type == 5) ids.push(id);
        layers++;
    }
    return ids;

}

function deleteDocAncestors(document) {
    if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
    var xmp = new XMPMeta(document.xmpMetadata.rawData);
    xmp.deleteProperty(XMPConst.NS_PHOTOSHOP, "DocumentAncestors");
    document.xmpMetadata.rawData = xmp.serialize();
    $.writeln("Cleaned Ancestors...")
}

function openSmartObject() {
    try {
        var idplacedLayerEditContents = stringIDToTypeID("placedLayerEditContents");
        var desc2 = new ActionDescriptor();
        executeAction(idplacedLayerEditContents, desc2, DialogModes.NO);

        return app.activeDocument;
    }
    catch (e) {
        alert(e.name + " " + e.message);
        return undefined;
    }
};

function removePSB(path) {
    var file = new File(path);
    if (file.exists) {
        file.remove();
        $.writeln("Removed file: " + path);
    }
    file.close();
}

function getSmartObjectFileExtension(id) {
    try {
        var r = new ActionReference();
        r.putProperty(stringIDToTypeID("property"), stringIDToTypeID("smartObject"));
        r.putIdentifier(stringIDToTypeID("layer"), id);
        var name = executeActionGet(r).getObjectValue(stringIDToTypeID("smartObject")).getString(stringIDToTypeID("fileReference"));

        var n = name.lastIndexOf(".");
        if (n < 0) return "";

        return name.substr(n + 1).toLowerCase();
    }
    catch (e) {
        $.writeln(e);
        return "error";
    }
}

function layerIsPhotoshopObject(layerID) {
    var ext = getSmartObjectFileExtension(layerID);
    $.writeln("Object with ID " + layerID + " Ext: " + ext);
    switch (ext) {
        case "psb":
        case "psd":
        case "png":

            return true;
        default:
            return false;
    }
}

 

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
Explorer ,
Aug 19, 2022 Aug 19, 2022

exiftool can already do this: exiftool -r -overwrite_original -XMP-photoshop:DocumentAncestors= [filepath.psd]

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
Mentor ,
Aug 19, 2022 Aug 19, 2022

No, it cannot.

This topic is about DocumentAncestors in smart objects nested in a document. Exiftool works only with the metadata block of the main document.

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
Explorer ,
Oct 18, 2022 Oct 18, 2022

Ahh, that's unfortunate. Still, there is a workaround: make smart objects into linked PSB assets, run the exiftool on that, then re-embed them into the PSD. Not as elegant, but it would work.

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