Skip to main content
Known Participant
October 31, 2008
Question

Can I automate the writing of XMP metadata into JPEG and TIFF files?

  • October 31, 2008
  • 91 replies
  • 11728 views
I have written an ASP.NET 3.5 website application on behalf of an annual international photographic competition. Entrants will be uploading digital photos in either JPEG or TIFF format. Ideally, I would write entrant identity and image title information into the XMP metadata for each image immediately after upload - but so far, I have failed to find any way to do this in ASP.NET.

Thousands of images are involved, so I need to find a way to automate the metadata insertion, perhaps with some sort of script that uses a text file (extracted from the SQL Server database on my website) as the source of the metadata for a batch of images. Is this the sort of task that can be done by writing a script for Bridge CS3? Are there any scripts already in existence that I could use? I am a total beginner in this area.

I use a Win XP PC, though I have a colleague who, I think, has CS3 on his Mac (running under the Leopard OS), so scripts for either platform might be usable.

David
This topic has been closed for replies.

91 replies

Paul Riggott
Inspiring
January 15, 2009
Thank you very much David, it's always good to get great information!
Participant
October 30, 2009

Hi Paul,

Thanks for the great scripts. I can use this and your import kewords script to automate quite a bit of data entry.

How can the metadata import fields be changed? I would like to be able to import data into a couple more fields but don't know much about scripts.

I am guessing things need to change around here

var titleAuthor= strInputLine.substr(strInputLine.indexOf(",")+1);
       titleAuthor = titleAuthor.replace(/&/g,"&");
   inputArray  = strInputLine.split(",");
   inputArray2  = titleAuthor.split(",");
   var csvFile = new File(inputArray[0]);
   var title = inputArray2[0];
   var author = inputArray2[1];
   var Country = inputArray2[2];

to add to the description field.

Thanks.

Joe

Paul Riggott
Inspiring
October 30, 2009

It's a long time since I looked at this but think this is the code with the Description field added for you..

#target bridge 
   if( BridgeTalk.appName == "bridge" ) { 
addInfo = MenuElement.create("command", "Add CSV Information", "at the end of Thumbnail");
}
addInfo .onSelect = function () {
   TitleAuthorCountryDesc();
}

function TitleAuthorCountryDesc(){
var value = 0;
var win = new Window("palette{text:'Please wait...',bounds:[100,100,550,140]," +
               "progress:Progressbar{bounds:[20,10,430,28] , minvalue:0,value:" + value + "}" +
               "};"
         );
var csv = File.openDialog("Please select CSV file.","CSV File:*.csv");
csv.encoding = "UTF-8";
if(csv != null){
csv.open("r");
var csvtemp=csv.read();
csvtemp =csvtemp.split("\n");
var maxcount = csvtemp.length;
csv.close();
csv.open("r");
win.progress.maxvalue =maxcount;
while(!csv.eof){ 
   win.center();
   win.show();
   win.hide();
   win.show();
   win.progress.value++; 
   strInputLine = csv.readln();
   if (strInputLine.length > 3) {
    strInputLine = strInputLine.replace(/\\/g,'/');
var titleAuthor= strInputLine.substr(strInputLine.indexOf(",")+1);
    titleAuthor = titleAuthor.replace(/&/g,"&");
   inputArray  = strInputLine.split(",");
   inputArray2  = titleAuthor.split(",");
   var csvFile = new File(inputArray[0]);
   var title = inputArray2[0];
   var author = inputArray2[1];
   var Country = inputArray2[2];
   var Description = inputArray2[3];
if(csvFile.exists){
   item = new Thumbnail(csvFile);
   md =item.synchronousMetadata; 
   var result =addTitleAuthor(md,inputArray2[0],inputArray2[1],inputArray2[2],inputArray2[3]);
   }
  }
}
}
win.close(2);
errorlog.close();
errorlog.execute();
}

function addTitleAuthor(metadata, Title, Author,Country,Description) {
var strTmpl = "TempTmpl";
var strUser = Folder.userData.absoluteURI;
var filTmpl = new File(strUser + "/Adobe/XMP/Metadata Templates/" + strTmpl + ".xmp"); 
var fResult = false;

if (filTmpl.exists) filTmpl.remove();
try{
fResult = filTmpl.open("w");
}catch(e){
  alert("Unable to create Template " +filTmpl +"\r"+e.message);
  return;
}
try{
if (fResult){
filTmpl.writeln("<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"3.1.2-113\">");
filTmpl.writeln("<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">");
filTmpl.writeln("<rdf:Description rdf:about=\"\"    xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\" photoshop:Country=\""+Country +"\">");
filTmpl.writeln("<dc:title>");
filTmpl.writeln("<rdf:Alt>");
filTmpl.writeln("<rdf:li xml:lang=\"x-default\">"+Title+"</rdf:li>");
filTmpl.writeln("</rdf:Alt>");
filTmpl.writeln("</dc:title>");
filTmpl.writeln("<dc:description>");
filTmpl.writeln("<rdf:Alt>");
filTmpl.writeln("<rdf:li xml:lang='x-default'>"+Description+"</rdf:li>");
filTmpl.writeln("</rdf:Alt>");
filTmpl.writeln("</dc:description>");
filTmpl.writeln("<dc:creator>");
filTmpl.writeln("<rdf:Seq>");
filTmpl.writeln("<rdf:li>" + Author + "</rdf:li>");
filTmpl.writeln("</rdf:Seq>");
filTmpl.writeln("</dc:creator>");
filTmpl.writeln("</rdf:Description>");
filTmpl.writeln("</rdf:RDF>");
filTmpl.writeln("</x:xmpmeta>");
fResult = filTmpl.close();
metadata.applyMetadataTemplate(strTmpl, "replace");
} }
catch(e) {
alert("There was a problem with the Template");
fResult = false;
}
return fResult;
};

dfranzen_camera_raw
Adobe Employee
Adobe Employee
January 15, 2009
Paul,

Right, XMPScript is only available in CS3 and CS4, and not for CS2. I just wanted to add an example of how to set a LangAlt and struct field using XMPScript, because those are two areas where folks might be confused.

Also, for a server-side hosted solution, folks might want to look into using the XMP Toolkit SDK. It includes the C++ implementation of XMPFiles for Windows and Mac. The C++ and JavaScript APIs for XMPFiles are as close to identical as is probably possible given the differences between the two languages.

-David
Paul Riggott
Inspiring
January 15, 2009
Thanks David, yes but this was for CS2 and I didn't think you could use AdobeXMPScript is this the case?
dfranzen_camera_raw
Adobe Employee
Adobe Employee
January 15, 2009
Paul,<br /><br />I think perhaps one of the problems with our original approach, using XMPScript, is that the Dublin Core "title" property is a LangAlt type, and not a simple array, so...<br /><br />xmp.appendArrayItem(XMPConst.NS_DC, "title", title, 0,XMPConst.ARRAY_IS_ORDERED); <br /><br />Is not setting the metadata correctly. I see that in the versions of the scripts where you are creating XMP XML, you are using the LangAlt type, for example...<br /><br />filTmpl.writeln("<rdf:li xml:lang=\"x-default\">"+Title+"</rdf:li>"); <br /><br />Instead you should use the setLocalizedText() function. Here is an example that uses the setLocalizedText function...<br /><br />#target bridge<br /><br />// Load XMPScript if it's not already<br />if( ! ExternalObject.AdobeXMPScript ) {<br /> ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");<br />}<br /><br />var x = new XMPMeta();<br /><br />$.writeln( $.locale );<br /><br />try {<br /> x.appendArrayItem( XMPConst.NS_DC, "creator", "John Doe", 0, XMPConst.ARRAY_IS_ORDERED );<br /> x.setLocalizedText( XMPConst.NS_DC, "title", null, "en-US", "Hello World" );<br /><br />} catch( e ) {<br /> $.writeln("ERROR: " + e );<br />}<br /><br />$.writeln( x.dumpObject() );<br /><br />This will produce the following output in the JavaScript console in ESTK:<br /><br />Dumping XMPMeta object "" (0x0)<br /><br /> dc: http://purl.org/dc/elements/1.1/ (0x80000000 : schema)<br /> dc:creator (0x600 : isOrdered isArray)<br /> [1] = "John Doe"<br /> dc:title (0x1E00 : isLangAlt isAlt isOrdered isArray)<br /> [1] = "Hello World" (0x50 : hasLang hasQual)<br /> ? xml:lang = "x-default" (0x20 : isQual)<br /> [2] = "Hello World" (0x50 : hasLang hasQual)<br /> ? xml:lang = "en-US" (0x20 : isQual)<br /><br />Adobe apps (at least up to CS4, which is the latest as I write) select and set only the "x-default" member of LangAlt arrays, however the setLocalizedText function seems picky and requires that you specify an actual language string -- and you should use a valid RFC 3066 string, like "en-US" (or one local to you). Whatever string you use, the x-default one will be set. <br /><br />Here is another example, that extends the above, using XMPFiles to write the XMP into a JPEG file. I also added an example setting one of the IPTC Core Creator Contact Info fields, since this uses another helper function from XMPScript, setStructField.<br /><br />#target bridge<br /><br />// Load XMPScript if it's not already<br />if( ! ExternalObject.AdobeXMPScript ) {<br /> ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");<br />}<br /><br />// Make a JPEG for test purposes<br />var curFolder = new File( $.fileName ).parent;<br />var jpeg = new File( curFolder.fsName + "/TestMe.jpg" );<br /><br />var bitmap = new BitmapData( 300, 200 );<br /><br />app.syncronousMode = true;<br />bitmap.exportTo( jpeg );<br />app.syncronousMode = false;<br /><br />var x = new XMPMeta();<br /><br />try {<br /> <br /> x.appendArrayItem( XMPConst.NS_DC, "creator", "John Doe", 0, XMPConst.ARRAY_IS_ORDERED );<br /> x.setLocalizedText( XMPConst.NS_DC, "title", null, "en-US", "Hello World" );<br /> <br /> // Set one of the IPTC Core reator properties<br /> x.setStructField( XMPConst.NS_IPTC_CORE, "CreatorContactInfo", XMPConst.NS_IPTC_CORE, "CiAdrCtry", "United States of America" );<br /><br /> $.writeln( x.dumpObject() );<br /><br /> // Opent he file with XMPFiles<br /> var xmpFile = new XMPFile( jpeg.fsName, XMPConst.FILE_UNKNOWN, XMPConst.OPEN_FOR_UPDATE | XMPConst.OPEN_USE_SMART_HANDLER );<br /><br /> if( xmpFile.canPutXMP( x ) )<br /> {<br /> xmpFile.putXMP(x);<br /> }<br /><br /> xmpFile.closeFile(XMPConst.CLOSE_UPDATE_SAFELY);<br /><br />} catch( e ) {<br /> $.writeln("ERROR SETTING METADATA: " + e );<br />}<br /><br />-David<br /><br />-David
Paul Riggott
Inspiring
January 12, 2009
I would think that it should work on a Mac but haven't tested it. You shouldn't need to change anything.
All the best, Paul.
Known Participant
January 12, 2009
Paul,
A quick test suggests that you have done a perfect job of implementing both of my requests. Once again, let me say how grateful I am for your assistance. The closing date for entries to our international photo salon is the end of January. and this script will be extremely useful.

BTW, does this script run equally well on a Mac? If so, are there any prerequisites for this to work? I use a PC but my colleague uses a Mac of some flavour or other.

David
Paul Riggott
Inspiring
January 12, 2009
Ok I have updated the script to add the Country field (last field) I have also attempted to include a progress bar (First time) hope it works.
Same as last time the updated script can be found here:-
http://ps-scripts.com/bb/viewtopic.php?p=10463#10463

Hope you have fun reading the new book!
Paul
Known Participant
January 12, 2009
Paul,
I ran your script successfully last night against a list of over 600 image files. It took 13 minutes to complete the process, which highlighted the fact that there is no progress indication of any sort, not even an hourglass symbol. I can live with that, but is there an easy fix?

Also, would it be easy to add the Country field to the metadata list, i.e. in addition to Title and Author?

BTW, I bought myself a book on Javascript but have not yet had time to read it!

Regards,
David
Known Participant
January 10, 2009
Paul,
Thanks for the rapid reply.

David
Paul Riggott
Inspiring
January 10, 2009
Just tested that version on CS4 and it still works fine.
All the best Paul.