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! 😀
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
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.
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
Thanks Peter! I’m really trying to make this blog useful 🙂
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é
Thanks tomaxxi,
Curious though, how CAN you set how many pages of preview you want InDesign to create and save?
Nik
Nik,
Unfortunately, there is no way to set number of pages through scripting.
Hope that helps.