XMPの利用方法
このところ夜は涼しいめで睡眠が捗ります。ようやく秋の気配が感じられる様になってきました。というわけで週末はぶどうを狩りに行こうと思っています。なんでも今年はつぶが大きくて甘い目だと、要するに豊作らしいです。死ぬ程食べてきます。娘はまだ無料の年齢ですが、食べ出すとすごいのでこちらも楽しみです。
話は変わりますが、うちのブルーベリーさん今年の酷暑をなんとか乗り切ったようです。ブルーベリー育てるには南国過ぎるので毎年根をやられて株がへたり、春先のシュートをなんとか育てるも真夏の暑さで振り出しへ…と言った駄目なサイクルをようやく断ち切る事が出来たようです。と言う事なので今年の冬はもうひと鉢増やそうかと思っている次第です。
本題です、XMPをスクリプトでごにょごにょするのは以前にもやってますが、今回はサムネイルを抜き出してみます。
コードから見てみましょう。
var re = /<xmpGImg:image>(.*)<\/xmpGImg:image>/;
var tgFile = File.openDialog(“Select source file”);
if(tgFile.open(‘r’)){
tgFile.read().match(re);
tgFile.close();
var str = RegExp.$1.replace (/
/g,”\n”);
var rt = decB64(str).substr (9, result.length-9);
var rslt = new File (‘~/desktop/preview_for_’ + tgFile.name + ‘.jpg’);
rslt.encoding = ‘binary’;
rslt.open (‘w’);
rslt.write (rt);
rslt.close();
}
function decB64(input) {
var keyStr = “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=”;
var reslt = “”;
var c1, c2, c3 = “”;
var e1, e2, e3, e4 = “”;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, “”);
while (i<input.length){
e1 = keyStr.indexOf(input.charAt(i++));
e2 = keyStr.indexOf(input.charAt(i++));
e3 = keyStr.indexOf(input.charAt(i++));
e4 = keyStr.indexOf(input.charAt(i++));
c1 = (e1 << 2) | (e2 >> 4);
c2 = ((e2 & 15) << 4) | (e3 >> 2);
c3 = ((e3 & 3) << 6) | e4;
result += String.fromCharCode(c1);
if (e3!=64) result += String.fromCharCode(c2);
if (e4!=64) result += String.fromCharCode(c3);
c1 = c2 = c3 = “”;
e1 = e2 = e3 = e4 = “”;
}
return result;
}
結果は以下の様な感じ。
AIのファイルは以下の様な構造です。
%PDF-1.5
%âãÏÓ
1 0 obj
<</Metadata 2 0 R/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<</Length 46885/Subtype/XML/Type/Metadata>>stream
<?xpacket begin=”” id=”W5M0MpCehiHzreSzNTczkc9d”?>
<x:xmpmeta xmlns:x=”adobe:ns:meta/” x:xmptk=”Adobe XMP Core 5.0-c060 61.134777, 2010/02/12-17:32:00 “>
<rdf:RDF xmlns:rdf=”http://www.w3.org/1999/02/22-rdf-syntax-ns#”>
<rdf:Description rdf:about=””
xmlns:dc=”http://purl.org/dc/elements/1.1/”>
<dc:format>application/vnd.adobe.illustrator</dc:format>
<dc:title>
<rdf:Alt>
<rdf:li xml:lang=”x-default”>testXMP</rdf:li>
</rdf:Alt>
</dc:title>
</rdf:Description>
<rdf:Description rdf:about=””
xmlns:xmp=”http://ns.adobe.com/xap/1.0/”
xmlns:xmpGImg=”http://ns.adobe.com/xap/1.0/g/img/”>
<xmp:MetadataDate>2012-08-30T10:05:52+09:00</xmp:MetadataDate>
<xmp:ModifyDate>2012-08-30T10:05:52+09:00</xmp:ModifyDate>
<xmp:CreateDate>2012-08-30T10:05:52+09:00</xmp:CreateDate>
<xmp:CreatorTool>Adobe Illustrator CS5</xmp:CreatorTool>
<xmp:Thumbnails>
<rdf:Alt>
<rdf:li rdf:parseType=”Resource”>
<xmpGImg:width>256</xmpGImg:width>
<xmpGImg:height>112</xmpGImg:height>
<xmpGImg:format>JPEG</xmpGImg:format>
<xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/
・
・
・
VJ9U1UDTr19ShsEuIxaNcveyX3N4jEa0kmKDevCg98aW2X6Botpoeh6fo1mzvaabbxWkDSkGQx
woEUsVCgmi70GFCPxV2KuxV2KuxV2KuxV2KuxV2KuxV//9k=</xmpGImg:image>
</rdf:li>
</rdf:Alt>
</xmp:Thumbnails>
</rdf:Description>
<rdf:Description rdf:about=””
xmlns:xmpMM=”http://ns.adobe.com/xap/1.0/mm/”
xmlns:stRef=”http://ns.adobe.com/xap/1.0/sType/ResourceRef#”
xmlns:stEvt=”http://ns.adobe.com/xap/1.0/sType/ResourceEvent#”>
<xmpMM:InstanceID>uuid:083a3f69-c33c-8049-bd86-c9973fa67839</xmpMM:InstanceID>
<xmpMM:DocumentID>xmp.did:F77F117407206811822A94CA2719E400</xmpMM:DocumentID>
<xmpMM:OriginalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</xmpMM:OriginalDocumentID>
・
・
・
まあ、AIに限らずアドビのデータをテキストエディタで開いてみると上のサンプルの様にサムネイルのデータがタグにはさまれて存在しているのは一目瞭然ですね。これを正規表現で抽出するだけです。このバイナリはクリエイターが8BIMのJFIF形式(jpegですね)なのですが、base64エンコード処理されていますのでデコード処理が必要です。この処理を行うのがdecB64ファンクションです。
AIに限らずPDF等XMPをサポートするAdobeのファイルはほとんどが同様の手法で抜き出せます。
注意として、容量の小さいファイルを処理しているので全体を読込んでいますが、大きなファイルの場合は頭からラインごとにフェッチして該当データを探す方が良いです。
まあ、今回もExternalObjectは利用しませんでした。というか必要がないと言った方が良いかな。ちなみに同様の処理をExternalObjectを利用してやると…
var myFile = File.openDialog(“Select source file”);
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);
ExternalObject.AdobeXMPScript.unload();
}
if(myXmp){
var myPreviews = “”;
thumbCnt = myXmp.countArrayItems(XMPConst.NS_XMP,”Thumbnails”);
if(thumbCnt!=0){
XMPnode = “Thumbnails”;
}else{
alert(“No Data saved”);
exit();
}
var myTemp = String(myXmp.getProperty(XMPConst.NS_XMP, XMPnode+'[‘+1+’]/xmpGImg:image’));
myTemp = myTemp.replace(“/*missing*/”,”\n”);
myPreview = decB64(myTemp).substr (9, result.length-9);
myOutput = new File (‘/preview_for_’ + myFile.name + ‘.jpg’);
myOutput.encoding = ‘binary’;
myOutput.open (‘w’);
myOutput.write (myPreview);
myOutput.close();
}
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;
}
function decB64(input) {
var keyStr = “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=”;
var reslt = “”;
var c1, c2, c3 = “”;
var e1, e2, e3, e4 = “”;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, “”);
while (i<input.length){
e1 = keyStr.indexOf(input.charAt(i++));
e2 = keyStr.indexOf(input.charAt(i++));
e3 = keyStr.indexOf(input.charAt(i++));
e4 = keyStr.indexOf(input.charAt(i++));
c1 = (e1 << 2) | (e2 >> 4);
c2 = ((e2 & 15) << 4) | (e3 >> 2);
c3 = ((e3 & 3) << 6) | e4;
result += String.fromCharCode(c1);
if (e3!=64) result += String.fromCharCode(c2);
if (e4!=64) result += String.fromCharCode(c3);
c1 = c2 = c3 = “”;
e1 = e2 = e3 = e4 = “”;
}
return result;
}
こんな感じになります。操作がわりと煩雑です。XMPデータの再構築が必要なければREで必要なデータのみ引っぱってくる方が楽だったりしますね。しかしながらメリットが無いわけではありません。XMPパケットしか取得しませんのでメモリマネージメント的にはこちらの方がスマートです。
あとコンストラクタ見ると分かるのですが、このスクリプトではアプリケーションパッケージ内のフレームワークにダイレクトにアクセスします。ですからIllustrator等のAdobeXMP.frameworkを内包するアプリケーションでないと動作しません。それと、ExternalObjectの複数インスタンスの生成は止めましょう。