Home > HOT Posts!, InDesign Scripts > Extract file preview stored in Adobe XMP data

Extract file preview stored in Adobe XMP data

Some of Adobe file format’s (INDD, PDF, AI) are using Metadata to store low-res preview of file inside file itself. For example InDesign allows you to select how many pages do you want to include in these previews and also preview size/quality. We have to keep in mind that data is not stored in binary format. It’s stored in, so called, Base64 encoding scheme, that represent binary data in an ASCII string format. So, in addition to extracting data we have to decode it to binary, and then save it to file. So, let’s get started! 🙂

For Base64 decoding I used script that can be found here: Base64 Encoding/Decoding. I deleted parts that we don’t need (for encoding), and also some “test/alert” parts, but it’s working just fine! 😀

function decode64(input) {
    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var output = "";
    var chr1, chr2, chr3 = "";
    var enc1, enc2, enc3, enc4 = "";
    var i = 0;
    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    do {
        enc1 = keyStr.indexOf(input.charAt(i++));
        enc2 = keyStr.indexOf(input.charAt(i++));
        enc3 = keyStr.indexOf(input.charAt(i++));
        enc4 = keyStr.indexOf(input.charAt(i++));
        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;
        output = output + String.fromCharCode(chr1);
        if (enc3 != 64) {output = output + String.fromCharCode(chr2);}
        if (enc4 != 64) {output = output + String.fromCharCode(chr3);}
        chr1 = chr2 = chr3 = "";
        enc1 = enc2 = enc3 = enc4 = "";
    } while (i < input.length);
    return output;
}

Extract preview from active document

Now, when we have our Base64 decode function, we can start extracting preview saved in active document with help of metadataPreferences. One thing that we have to keep in mind is that XMP node name was changed in CS5. In pre-CS5 node name is Thumbnails and in CS5 is PageInfo, so we have to keep script working we have to check for both values. Dont worry, file can’t have both nodes. Keep in mind that decode time will increase with preview size. For more info how script works, read coments inside script.

UPDATE

I just noticed that syntax highlighter deleted one part of script. In code where you see /*missing*/ replace with this (it’s XML Whitespace):

// XMP namespace where preview is saved
var XMPnamespace = "http://ns.adobe.com/xap/1.0/";
var myPreviews = Array();

var docName = app.activeDocument.name;
// extract just file name without extension from document name
docName = docName.replace(/.[^.]+$/,'');

// count number of previews in both XML nodes
thumbCnt = app.activeDocument.metadataPreferences.countContainer(XMPnamespace,"Thumbnails");
pagInfCnt = app.activeDocument.metadataPreferences.countContainer(XMPnamespace,"PageInfo");

// check and set retrieved values
if(thumbCnt != 0){
    pagePrevCnt = thumbCnt;
    XMPnode = "Thumbnails";
}else if(pagInfCnt != 0){
    pagePrevCnt = pagInfCnt;
    XMPnode = "PageInfo";
}else{
    alert("No Data saved");
    exit();
}

// loop through available nodes, and retrieve data
for(var i = 1; i <= pagePrevCnt; i++){
    var myTemp = app.activeDocument.metadataPreferences.getProperty(XMPnamespace, XMPnode+"[" + i + "]/xmpGImg:image");
    myTemp = myTemp.replace("/*missing*/","\n");
    myPreviews.push(myTemp);
}

// loop through previews and save them to files
for(var i = 0; i < myPreviews.length; i++){
    myPreview = decode64(myPreviews[i]);
    myOutput = new File ('/c/Preview for ' + docName + ' No. ' + (i + 1) + '.jpeg');
    myOutput.encoding = 'binary';
    myOutput.open ('w');
    myOutput.write (myPreview);
    myOutput.close();
}

Extract preview from placed file

If you place PDF, AI, INDD file into InDesign, you can also extract previews saved in files. This time, we have to use ExternalObject because itemLink.linkXmp.countContainer is not working (I don’t know why)! For more info about ExternalObject check these posts: Extract Metadata with Adobe XMP [Part 1] and Extract Metadata with Adobe XMP [Part 2]. Structure of this function is almost the same as previous.

// load XMP Library
function loadXMPLibrary(){
    if ( !ExternalObject.AdobeXMPScript ){
        try{ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');}
        catch (e){alert('Unable to load the AdobeXMPScript library!'); return false;}
    }
    return true;
}

// check selection
if(loadXMPLibrary() && app.selection.length == 1 && app.selection[0].contentType == ContentType.GRAPHIC_TYPE){
    var myFile = File(app.selection[0].graphics[0].itemLink.filePath);
    // load XMP from file
    xmpFile = new XMPFile(myFile.fsName, XMPConst.UNKNOWN, XMPConst.OPEN_FOR_READ);
    var myXmp = xmpFile.getXMP();
    //close file
    xmpFile.closeFile(XMPConst.CLOSE_UPDATE_SAFELY);
}

if(myXmp){
    var myPreviews = Array();

    // node checks
    thumbCnt = myXmp.countArrayItems(XMPConst.NS_XMP,"Thumbnails");
    pagInfCnt = myXmp.countArrayItems(XMPConst.NS_XMP,"PageInfo");

    // data check
    if(thumbCnt != 0){
        pagePrevCnt = thumbCnt;
        XMPnode = "Thumbnails";
    }else if(pagInfCnt != 0){
        pagePrevCnt = pagInfCnt;
        XMPnode = "PageInfo";
    }else{
        alert("No Data saved");
        exit();
    }

    // loop through preview nodes
    for(var i = 1; i <= pagePrevCnt; i++){
        var myTemp = String(myXmp.getProperty(XMPConst.NS_XMP, XMPnode+"[" + i + "]/xmpGImg:image"));
        myTemp = myTemp.replace("/*missing*/","\n");
        myPreviews.push(myTemp);
    }

    // save previews to files
    for(var i = 0; i < myPreviews.length; i++){
        myPreview = decode64(myPreviews[i]);
        myOutput = new File ('/c/Preview for ' + myFile.name + ' No. ' + (i + 1) + '.jpeg');
        myOutput.encoding = 'binary';
        myOutput.open ('w');
        myOutput.write (myPreview);
        myOutput.close();
    }
}

Extract preview from file you select

Last example is focused on extracting previews from file that you select with Javascript file open dialog. This script is same as previous, with just one change for open dialog, instead loading file from selected link.

function loadXMPLibrary(){
    if ( !ExternalObject.AdobeXMPScript ){
        try{ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');}
        catch (e){alert('Unable to load the AdobeXMPScript library!'); return false;}
    }
    return true;
}

var myFile = File.openDialog("Select source file", undefined, false);

if(loadXMPLibrary() && myFile != undefined){
    xmpFile = new XMPFile(myFile.fsName, XMPConst.UNKNOWN, XMPConst.OPEN_FOR_READ);
    var myXmp = xmpFile.getXMP();
    xmpFile.closeFile(XMPConst.CLOSE_UPDATE_SAFELY);
    unloadXMPLibrary();
}

if(myXmp){
    var myPreviews = Array();

    thumbCnt = myXmp.countArrayItems(XMPConst.NS_XMP,"Thumbnails");
    pagInfCnt = myXmp.countArrayItems(XMPConst.NS_XMP,"PageInfo");

    if(thumbCnt != 0){
        pagePrevCnt = thumbCnt;
        XMPnode = "Thumbnails";
    }else if(pagInfCnt != 0){
        pagePrevCnt = pagInfCnt;
        XMPnode = "PageInfo";
    }else{
        alert("No Data saved");
        exit();
    }

    for(var i = 1; i <= pagePrevCnt; i++){
        var myTemp = String(myXmp.getProperty(XMPConst.NS_XMP, XMPnode+"[" + i + "]/xmpGImg:image"));
        myTemp = myTemp.replace("/*missing*/","\n");
        myPreviews.push(myTemp);
    }

    for(var i = 0; i < myPreviews.length; i++){
        myPreview = decode64(myPreviews[i]);
        myOutput = new File ('/c/Preview for ' + myFile.name + ' No. ' + (i + 1) + '.jpeg');
        myOutput.encoding = 'binary';
        myOutput.open ('w');
        myOutput.write (myPreview);
        myOutput.close();
    }
}

And here is what we getL (This image is extracted from InDesign CS5 file, with preview size set to extra large)

And at the end, it’s also good idea to take a look how to enable previews for InDesign files through scripting. Like I said on beginning, we can set how many pages you want InDesign to create and save, but, unfortunately, it can’t be done through scripting (for now). What we can do is to enable/disable previews and set preview size like this:

// enable previews
app.generalPreferences.includePreview = true;

// set preview size
app.generalPreferences.previewSize = PreviewSizeOptions.LARGE;

If we use smaller preview sizes, some small text will be greeked. Here are available preview size options we can use: (keep in mind that increasing preview size will increase InDesign file size too)

PreviewSizeOptions
PreviewSizeOptions.SMALL
PreviewSizeOptions.MEDIUM
PreviewSizeOptions.LARGE
PreviewSizeOptions.EXTRA_LARGE

Well, that’s it, hope you learned something.

Have fun! 😀

  1. September 3, 2010 at 18:09

    Hello Tomaxxi,

    Interesting implementation. Here’s another version that came up in the forum last year:

    http://forums.adobe.com/message/1109879#1109879

    Regards,

    Peter

    • September 3, 2010 at 18:19

      Thanks Peter!

      I saw that post, and also contacted Thomas about it. However, that script is not working with CS5 because of XMP node name changed for preview as I mentioned in article. Also I found shorter Base64 decode function 🙂

      Thanks again!

      Regards, Marijan.

  2. September 3, 2010 at 21:56

    Aha — I hadn’t realised that that other one didn’t work in CS5. But that doesn’t matter as your example is much better!

    Peter

    • September 3, 2010 at 22:15

      Thanks Peter! I’m really trying to make this blog useful 🙂

    • André S.Silva
      August 26, 2012 at 18:52

      Hello Peter,

      I was wondering if its possible to do this with objective-c without using QUICKLOOK plugins that generator the previews. Thank your for you time.

      Best regards, André

  3. Nik
    August 12, 2011 at 11:50

    Thanks tomaxxi,

    Curious though, how CAN you set how many pages of preview you want InDesign to create and save?

    Nik

    • December 14, 2011 at 16:14

      Nik,

      Unfortunately, there is no way to set number of pages through scripting.

      Hope that helps.

  1. September 7, 2010 at 01:57

Leave a comment