XMP Feature is available in Photoshop 25.0

XMP module in UXP offers JavaScript access to the Adobe XMP Core and XMP Files libraries. This feature is now available in UXP 7.2 with Photoshop 25.0.

For more details refer:
https://developer.adobe.com/photoshop/uxp/2022/uxp-api/reference-js/Modules/uxp/XMP/

@vinayKumarG @pkrishna

7 Likes

This is a great feature addition! Thanks so much!

Reading through the documentation I have one question. Maybe I missed this, but I wonder how XMP’s File I/O relates to Photoshop’s I/O? It seems to me like in order to access a document’s metadata you call XMP’s file I/O. Let’s assume I have a document open in Photoshop, modifying its contents, let’s say I also want to update its metadata. Would XMP’s file I/O use the file that’s already being worked on by Photoshop or open it again separately?

@dotproduct,
It depends on how you would update the XMP data, using the batchplay to update or use XMPFile

If you use batchplay, it would make use of the file already opened by photoshop, which is the recommended way for already opened files,

If you use XMPFile to write then it would open separately, recommend to use when you dont want to open the file in Photoshop

Great, thanks for clarifying! So using batchPlay I assume the idea would be to pass the serialized string to the XMPMeta constructor, correct?

Yes, passing serialized string to the XMPMeta constructor, modify the content on the object using the methods provided and pass it back as string in batchplay to update

Perfect. Thanks again for this feature, I’m sure it will make it much easier to work with metadata!

2 Likes

@vinayKumarG
Is it possible to provide working example of this since I am not being able to make it work and couldn’t also find anything in the docs.
What I am trying to do is

  1. Read the XMP data of the currently opened file in PS with batchplay
  2. Modify one of the properties(custom namespace)
  3. Save the XMP data to the currently opened file in PS

@freakpazo,

Here is a sample,


// Batchplay commands to get and set XMP
const bp = require("photoshop").action.batchPlay;
// GETTER
const getDocumentXMP = () => {
    return bp(
        [
            {
                _obj: "get",
                _target: {
                    _ref: [
                        { _property: "XMPMetadataAsUTF8" },
                        { _ref: "document", _enum: "ordinal", _value: "targetEnum" },
                    ],
                },
            },
        ],
        { synchronousExecution: true }
    )[0].XMPMetadataAsUTF8;
};

const psCore = require("photoshop").core;
const setDocumentXMP = async (xmpString) => {
    try {
        await psCore.executeAsModal(
            async () =>
                await bp(
                    [
                        {
                            _obj: "set",
                            _target: [
                                { _property: "XMPMetadataAsUTF8" },
                                { _ref: "document", _enum: "ordinal", _value: "targetEnum" },
                            ],
                            to: {
                                _obj: "XMPMetadataAsUTF8",
                                XMPMetadataAsUTF8: xmpString,
                            },
                        },
                    ],
                    { synchronousExecution: false }
                ),
            { commandName: "Setting XMP..." }
        );
    } catch (error) {
        console.error(error);
    }
};

function updateXMP(xmp) {
    xmp.appendArrayItem(XMPConst.NS_DC, "title", "Sample Title", 0, XMPConst.ARRAY_IS_ALTERNATIVE);
    xmp.appendArrayItem(XMPConst.NS_DC, "Creator", "Sample Creator Value", 0, XMPConst.ARRAY_IS_ORDERED);
    xmp.appendArrayItem(XMPConst.NS_DC, "description", "Set description Here", 0, XMPConst.ARRAY_IS_ALTERNATIVE);
    xmp.setProperty(XMPConst.NS_XMP, "Rating", 4); // Rating Number
    xmp.setProperty(XMPConst.NS_PHOTOSHOP, "AuthorsPosition", "Sample Author Position");
    xmp.setProperty(XMPConst.NS_PHOTOSHOP, "CaptionWriter", "Sample Caption Writer");
    return xmp;
}


// Fetch XMP Form Current Document
let xmpData = getDocumentXMP();
let meta = new XMPMeta(xmpData);
meta = updateXMP(meta);
setDocumentXMP(meta.serialize());

I’m try to run the sample batch script from the link that was provided to learn how this works. The sample script is giving 2 errors. The image files in the folder are jpg images that contain XMP metadata.

First error line-

if(file instanceof Entry)

ReferenceError: Entry is not defined

So I removed the if statement to just force it to run on each image file. Then it gives the next error

Second Error Line-

let xmpFile = new XMPFile( file.nativePath, XMPConst.UNKNOWN, XMPConst.OPEN_FOR_UPDATE );

Error: Bad argument type, usage:

Never check it like this. This is a way to go :slight_smile: https://developer.adobe.com/photoshop/uxp/2022/uxp-api/reference-js/Modules/uxp/Persistent%20File%20Storage/Entry/#isentry--boolean

Also make sure to use file:// protocol in file path. At least in current version.

@ddbell,

can you replace the condition in if to if(file.isFile),

we need this condition to work only on Files and not folders,

Also, the error posted could be caused by any files which doesn’t XMP Metadata,
if you still see the error, you might want to check for any hidden files in that folder.

The file.isFile fixed the if statement.

However, the other error still occurs. See the attached screenshot. It is happening on image files as can be seen in the debugger. I tried image files straight off the camera with the standard camera set metadata. I also tried with images that I added metadata to the copyright and descriptions fields in Photoshop through “file>file info” from the Photoshop menu. So those files have Photoshop XMP metadata added. For both types, it gives this error for all images.

For my own plugins, I always use .isFile to check to see if the entry is a file.

I was trying to test the script exactly “as is” from the Adobe documentation. I just assumed that Adobe had a specific reason for using (file instanceof Entry), but just didn’t know what the reason was.

Honestly , I didn’t realize they were just trying to make sure the entry was a file. The use of (file instanceof Entry) threw me off as I had never seen that before.

Problem with instanceof is that it really must be instance of the class you are testing against. E.g. if you would have 2 classes both with same name class Test then it will return true only for one of them but never both. And sometimes you might want this behavior. But in my experience it had more cons than benefits. The condition failed and it was hard to tell what was instance of what because those classes were identical but not the same :smiley:

Thanks for the info. I still can’t get the other part of the script to work. I assumed the script in the Adobe documents should work as is, but I still get the second error. I tried reading through the documentation page but it is confusing to me and not even sure what to change in their script.

I don’t need to edit meta data like the Adobe script does. I was just going through that to try to learn how it works, but got stuck because the script doesn’t work as is.

Ultimately, what I need to do seems like it should be simple. I just want to take the metadata from one file and then overwrite the metadata in a different file with that same metadata, unchanged.

@ddbell
errors is because, of the second argument XMPConst.UNKNOWN, you need to pass the type of the file, if you are using jpg files can you try passing XMPConst. FILE_JPEG , also you can refer to other values here XMPConst

Thanks, I was wondering about the “UNKNOWN” part. I will try it out.

However, I just was using the script exactly as is from the Adobe documentation.

At least for me, It is difficult based on the webpage documentation and script to understand what needed to go there in place of the “UNKNOWN”.

I will test it out and see what I get.

@ddbell thanks for trying it out and providing feedback, we will update it with more details, so that its easy to use and understand

Thank you for your help. I ran the script with the 2 modifications and it was successful. It did exactly what it was suppose to do to replace the creator/author metadata.

Now I need to dig further into it tomorrow to see if I can get it to work what I need to actually use it for. Hopefully once I get going on it then it will all start to make sense.

1 Like