MacOSXでツール開発

AppleScriptで小道具を作成
画像をJPEGに変換する小道具


●命令書の書き方、その第一歩

 プログラムの何たるか‥‥は、後々本屋さんでモノの本を買って読んでもらうとして、まずはとにかく、作ってみましょう。

 AppleScriptの「スクリプトエディタ」を立ち上げて、小道具の設計図=コンピュータへの命令書を作ります。もちろん、スクリプトエディタを立ち上げた直後は「白紙」の命令書が存在するだけです。

 通常、スクリプトエディタは下の参考画像のように、「アプリケーション」フォルダの「AppleScript」フォルダの中にあります。スクリプトエディタ(またはスクリプトエディタ.app)をダブルクリックして起動します。

 すると、以下のような白紙の命令書が表示されます。

 手始めに、白紙の命令書に「on open gazouFiles」と書いてみます。その後、「コンパイル」ボタン、すなわち「命令書の内容チェック」ボタンを押します。‥‥すると警告ウィンドウが表示されます。

←コンパイルボタンをクリック

 なぜこのようなエラー警告が出たのでしょうか。

 AppleScriptの命令書は、命令書の始まりである「on open 〜」を書いたら、命令書の終わり=「閉じ」を表記する必要があります。

 命令書の「閉じ」を指定するにはどうしたら良いか?

 ‥‥改行して「end」と書けばそれでOKです。

 もう1回コンパイルボタンをクリックしてみましょう。すると、以下の様にきれいに色分けされて、再表示されます。コンパイルボタンを押すと、コンピュータは命令文を内容チェックし、文面に問題が無い場合は自動で色分けをおこない再表示するのです。

 文書をよく見ると、「end」と書いただけなのに、「end open」に書き直されています。これはコンピュータが「正式な書式」に書き換えてくれた結果です。今の時点では、書き換えた内容自体は気にせず、どんどん先に行きましょう。


●ドラッグ&ドロップのアプリケーションを作る仕組み

 「on open、て何?」とまず疑問に思う事でしょう。「on open」とは「ドラッグ&ドロップ形式のアプリケーションを作る際の命令書式」です。お金を請求する時は「請求書」と書く様に、ドラッグ&ドロップ形式のアプリケーションを作る際は「on open」と書く決まりになっています。「きまりなんて、ヤダ」とか言わないでください。請求書を扱う際、「御社が私に支払う金額総計の告知書」なんていう書き方では通用しないのと同様に、AppleScriptにも決まりがあります。「請求書」と言う言葉が覚えられて、「on open」という言葉が覚えられない‥‥という事はありませんよね。

 命令書に「on open」と書いて、「end」で閉じれば、それはすなわちドラッグ&ドロップ形式のアプリケーションの命令書となります。その命令書をアプリケーション形式で書き出せば、「矢印のついたアイコンのAppleScriptアプリケーション」の出来上がりです。

 さて、その次。「on open」の次の「gazouFiles」はなんでしょうか。

 「on open gazouFiles」の「gazouFiles」は私が勝手に付けた言葉です。「決まりがあると言う割には、勝手な事もできるのか?」とお思いでしょうが、請求書のの名目がその時々によって変わるのと同様に、AppleScript命令書もその時々で対象となる名目を変える事ができます。

 今回は画像ファイルを相手に処理をおこなうので、その画像ファイルを「gazouFiles」(ガゾウファイルズ)と解りやすく「私の都合」で呼称したのです。別に「imageFiles」でも「theFiles」でも良いんですよ。命令書の中で一貫して呼び表されていれば、名目・品目呼称は設計者が自由に決められるのです。

 つまり、「on open gazouFiles」とは、「『gazouFiles』と一時的に名付けたドラッグ&ドロップしたファイル群に対し、これから命令を始める!」と書き込んでいる訳です。

on open gazouFiles
end open

 上記の2行さえあれば、ドラッグ&ドロップ形式のアプリケーションになる訳です。アプリケーションの体裁を整えるには、もっと数多くの段取りが必要だと何となく推測していたのだとしたら、それは単なる誤解です。‥‥少なくとも、AppleScriptではたった2行だけの段取りでOKです。


●画像ファイルを順々に処理する仕組み

 しかし、現時点での命令書には「ドラッグ&ドロップした項目を、どのように処理するか?」が一切書いてありません。JPEGに変換するのなら、そのように命令書に書く必要があります。

 ドラッグ&ドロップしたファイルは1つの時もあれば、2つ以上の時もあります。すなわち、最初から複数のファイルを処理する様にしておけば、何個のファイルにでも対応できます。

 2つ以上の項目を順々に処理するには、「繰り返し(Repaet)この文」を使います。JPEG変換する処理を、複数の項目に対して順番に適用する訳です。

 複数の項目を順番に処理する命令書は以下のような文面になります。on open gazouFilesとend openの間に命令を挿入している点に注目してください。

on open gazouFiles
 repeat with gazouFile in gazouFiles
 end repeat
end open

 ドラッグ&ドロップされた「gazouFiles」の中の項目を処理するのですから、on open gazouFilesとend openの中に命令を書き加える‥‥という訳です。

 この命令により、「gazouFiles 」でひとくくりに呼称されている複数項目を1つずつ取り出して、「gazouFile」と言う呼称で呼び表して処理する事が可能となりました。

 う〜む、ここらへんから、少々構造がよく解らなくなってきたでしょうか?‥‥何か他のものに例えて、構造を理解してみましょう。

 例えば、以下のように例えてみましょう。

◆お寿司屋さんで、食事◆

今日は、中トロとタコとヤリイカを注文した。

板前さんから、カウンターの上に順次、お寿司が差し出された。

 上記の様子を、AppleScriptで表現してみましょう。

on open |注文|
 repeat with |カウンターの上| in |注文|
 end repeat
end open

 「中トロとタコとヤリイカ」は「注文」という「ひとくくり」で扱われます。また、握り上がったお寿司は、「カウンターを介して」、「一品ずつ」お客さんに差し出されます。つまり、お客さんは「注文」という形で自分の欲しいお寿司をオーダーし、「カウンターの上」で受け取る訳です。注文と言う名前のお寿司を食べたい訳でもなければ、カウンターそのものを食べる訳でもありません。

 プログラムでは、扱う項目に対して一時的な呼び名を設定し、実名ではなく一時的な呼び名で扱う事が頻繁に出てきます。その一時的な呼び名の事=お寿司屋さんの例で言えば、「注文」「カウンターの上」を「変数」と言います。お寿司屋さんは「カウンターの上」を用いる事で、「注文」の中身の「お寿司」つまり中トロやヤリイカを、お客さんに効率良く渡す事が可能となります。

サンプルスクリプト「お寿司屋さん」

 ダウンロード・解凍すると「お寿司屋さん」フォルダができます。その中にある「寿司ネタ」名のフォルダをAppleScriptアプリケーション「お寿司を握ってもらう.app」のアイコンにドラッグ&ドロップしてください。注文したお寿司が順次表示されます。

 スクリプト文を見たい場合は、「お寿司を握ってもらう.app」をスクリプトエディタのアイコンにドラッグ&ドロップしてください。

 画像ファイルを順々に処理するお膳立ては整いました。次はいよいよ最終行程、画像ファイルをJPEGに変換する命令を作成します。


●画像ファイルをJPEGに変換する仕組み

 AppleScriptは自分自身の機能を使うのではなく、他のアプリケーションに対して命令し処理を実行します。「命令書」と比喩している所以はまさにそこにあります。

 画像ファイルをJPEGに変換する際、例えばPhotoshopではどのように処理するでしょうか。画像ファイルをPhotoshopで「開き」、「別名で保存」にてJPEG形式で保存、開いたファイルを「閉じる」‥‥こんな段取りでしょう。この段取りをAppleScriptから命令してやれば、アプリケーションはその命令通りに動作します。

 では「JPEGに変換する」処理を実行する際、どのアプリケーションに対して命令を送れば良いでしょうか?‥‥いくつか下に挙げてみましょう。

  • Photoshop 7.0Photoshop,CS,CS2(7.0以降)
  • Graphic Convertor
  • Image Events
  • sips
  • Image Magick
  • etc....

 ‥‥とまあ、選択肢は色々あります。Photoshopやグラフィックコンバーターは所有していないと使えませんし、sipsやImage Magickはシェルコマンドの知識が多少必要になりますので、今回はImage Eventsを使ってみましょう。

 Image EventsはPhotoshopとは違い、姿が見えません。処理だけをおこなう「裏方のアプリケーション」、つまり、AppleScriptで命令すると結果だけを返してくる、とてもシンプルなアプリケーションです。

 特定のアプリケーションに命令を送るには、以下の書式を使います。

tell application アプリケーション名 to 命令

 または以下の書式です。

tell application アプリケーション名
 命令
end tell

*「tell application」で命令先のアプリケーションを指定する事ができます。実はこの「tell」にはアプリケーションの指定だけではなく、他にも便利な使い方があるのですが、今は「tell」と「application」のセットの「tell application」で覚えておきましょう。

 今回はImage Eventsに命令を送りたいので、以下の様になります。

tell app "Image Evenst"
end

 「app」と書いているのは、applicationの省略形です。今は省略形を使わず正式な書き方で書きたい!‥‥と言う人は「application」と書いても構いませんよ。

 「Image Events」というアプリケーション名称を「"」で囲んでいる点に注目してください。名前などの語句をプログラムに書く時は、命令語句とそれ以外の語句とを明示的に分ける為、「"」で囲む必要があります。もし「"」でImage Eventsを囲まないと、AppleScriptは「Image Events」という語句をアプリケーション名ではなく、命令そのものとして認識しようとします。そうした誤解釈を避ける為に、あえて「"」で固有名詞等を囲んで明示するのです。

  • tell application Image Events →NG!
  • tell application "Image Events" →OK!

  • tell application iPhoto →NG!
  • tell application "iPhoto" →OK!

  • tell application Safari →NG!
  • tell application "Safari" →OK!

 Image Eventsへの命令を、今まで作って来た命令「on open gazouFiles〜」の中に組み込むと以下の様になります。

 コンパイルすると以下の様に色分けされます。

 今回は「画像ファイルそれぞれを受け取った後でImage Eventsに命令を送る」書式にしてみました。もう1つの方法として「Image Eventsを起動した上で、ファイルそれぞれを処理する」やり方もありますが、今回は「画像ファイルそれぞれ→Image Events」という段取りでいきましょう。

 Image Eventsへの命令文の最初の最初として、Image Eventsを起動する命令「launch」を命令文に組み込んでおきましょう。

on open gazouFiles
 repeat with gazouFile in gazouFiles
  tell application "Image Events"
   launch
  end tell
 end repeat
end open


●画像ファイルを開く、保存する、閉じる‥‥の段取り

 まず、Image Eventsで画像を開いてみましょう。‥‥と言っても、Image Eventsが画像を「内部的に」開くので、目に見える形では表れません。Image Eventsが裏方ならば、開いた画像も裏方で人目には見えない‥‥という訳です。

on open gazouFiles
 repeat with gazouFile in gazouFiles
  tell application "Image Events"
   launch
   open gazouFile
  end tell
 end repeat
end open

 画像ファイルは開いただけではなく、JPEGで保存して閉じる必要がありますが、開いた画像をどのように呼び表せば良いのでしょう?‥‥まさか、「さっき開いたファイル」と命令書の中に書く訳にもいきません。それにImage Eventsの画像保存命令は、ファイルそのものではなく、ファイルを開いて出現した画像に対して働きます。‥‥どうすれば良いでしょうか?

 答えは簡単。画像ファイルを開いた「結果」を利用すれば良いのです。

 例えば、Image Eventsがファイルを開くと、結果として開いた画像(image)を返します。つまり、ファイルを開いた結果である画像(image)を保存命令に引き継げば、JPEGで書き出す事が可能となります。

 では、結果を引き継ぐには、どのように命令書を書けば良いのか?

 先ほどチョコっと説明した「変数」を使えば良いのです。変数は「一時的な呼び名」と書きましたが、その時々の結果に対しても「一時的な呼び名」をつける事ができます。一時的な呼び名をつける‥‥すなわち、変数をセットするには「set」命令を使います。

set currentImage to open gazouFile

 変数「currentImage」(現在の画像という意味でつけた変数名)に「open gazouFile」の結果を収納しました。以降は、この「currentImage」という呼び名を用いて、命令文を書き進めます。

 さて今度はJPEG変換、有り体に言えば、開いた画像をJPEGで保存し直す事になります。保存命令文を書いてみましょう。

save currentImage as JPEG

 上記に命令文には、何か足りないものがあります。‥‥保存先です。「何と言うファイルで書き出すのか?」が明記されていません。ただ漠然と「JPEGで保存」としか書いていません。保存先のファイルを明示的に書かないと、結果的にオリジナルの上書き保存になって大変な事になります。

 新規保存先のファイルを指定するには、どのような書き方をすれば良いか?‥‥以下の様になります。

file 新規ファイルのパス

 ファイルのパスはMacOSの場合フォルダ階層を「:」で区切って記述します。つまり「ディスク『MacHD』のフォルダ『AppleScriptテスト』のファイル『テスト.jpg』」ならば「MacHD:AppleScriptテスト:テスト.jpg」となります。

 例えば、開いた画像ファイルをJPEGで「ディスク『MacHD』のフォルダ『AppleScriptテスト』のファイル『テスト.jpg』」として保存するならば、以下の様になります。

save currentImage as JPEG in file "MacHD:AppleScriptテスト:テスト.jpg"

 うまくいったような気がしますが、‥‥何か見落としがあるような???

 既にお気付きかも知れませんが、毎回同じ場所に同じ名前でJPEGを書き出す状況はあまり考えられませんよね?‥‥20個の画像ファイルをJPEG変換保存したら、同じ名前でどんどん上書き保存して、最後に保存したものだけが残る‥‥というトンチンカンな命令になってしまいます。

 では「変換対象の保存場所に変換対象オリジナル名+JPEG拡張子」にしてみてはどうでしょうか。

  • 変換前「MacHD:AppleScriptテスト:test_0001.psd」
  • 変換後「MacHD:AppleScriptテスト:test_0001.psd.jpg」

  • 変換前「MacHD:AppleScriptテスト:test_0002.psd」
  • 変換後「MacHD:AppleScriptテスト:test_0002.psd.jpg」

 まず、オリジナル画像ファイルのパス、つまり「変換対象の保存場所+変換対象オリジナル名」を、新たな変数を使って格納しておきます。ファイルのパスを取得するには「as Unicode Text」でファイルを文字列に変換します。

set fileMacPath to gazouFile as Unicode text

 その次に、新しいファイル、つまりJPEGで新規保存するファイルのパスを、先ほどオリジナル画像ファイルパスを格納した変数「fileMacPath」を用いて、さらに変数を新作します。末尾に「.jpg」を付けるには、「&」を用います。

set newFileMacPath to fileMacPath & ".jpg"

 「.jpg」を「"」で囲んである点に注意してください。既出の項でも書きましたが、コンピュータの命令文誤解釈を未然に防ぐ為には、「.jpg」という文字列が命令文以外である事を「"」で囲んで明示する必要があります。

 さて、オマケですが、Image Eventsにはアイコン保存機能がありますから、折角ですので保存と同時にアイコンも付けときましょう。もちろん、アイコンは後々面倒だからイラナイ!‥‥と言う人は付けなくても構いませんヨ。アイコンを付けるのは簡単、「with icon」を「save」命令文の行に追加するだけです。

save currentImage as JPEG in file newFileMacPath with icon

 これで書き出す準備は整いました。命令文を日本語で表現すると、「currentImageをJPEGとしてファイル newFileMacPathにアイコン付きで保存する」となります。‥‥まあ、英文を読んでもらえば、お解りですよね!

 ん?‥‥何か、忘れてないか???‥‥

 そう言えば、「画像を閉じる」を書いていませんでしたね。目に見えないからと言って、画像の開きっぱなしは「ちらかしっぱなし」みたいで気がひけます。ちゃんと閉じておきましょう。

close currentImage

 これですべて終了!

 命令文の全文は以下の様になります。

on open gazouFiles
 repeat with gazouFile in gazouFiles
  set fileMacPath to gazouFile as Unicode text
  set newFileMacPath to fileMacPath & ".jpg"
  tell application "Image Events"
   launch
   set currentImage to open gazouFile
   save currentImage as JPEG in file newFileMacPath with icon
   close currentImage
  end tell
 end repeat
end open


●最後に、動作試験

 命令文を書き換えたら、上書き保存しておきましょう。もしまだ1回も保存していない場合は、既出の上の項を参照してください。

 いよいよ、命令書を実行します。ドラッグ&ドロップ形式のアプリケーションですから、作ったアプリケーションのアイコンに画像ファイルをドロップして、命令書を実行します。実行テストにはテスト用画像ファイルを用いましょう。くれぐれも大事なファイルをぶっつけ本番で処理しないようにしてくださいね!

ドロップ中...

 結果は、めでたしめでたし! 命令書の意図通りの仕上がりとなりました。

 JPEGとして新規保存したファイルはちゃんとオリジナルと同階層に存在し、名前も末尾に「.jpg」拡張子がついたファイルとなっています。簡単な変換程度だったらいちいちPhotoshopを用いなくても、MacOSX(10.3以降)の機能だけで充分可能なんですよ。

 命令文はアプリケーションとして保存してありますから、いつでも画像ファイルをドラッグ&ドロップして命令実行する事ができます。たった12行の命令文ですが、いつでも手軽にJPEG変換できる「頼もしい脇役アプリケーション」が出来上がりました。1個でも100個でも、画像ファイルならJPEGに変換してくれます。

 ただし、今回の12行の命令文にはまだ「穴」があります。画像ファイルではなくテキストファイルをドロップしたらどうなるのか、フォルダをドロップしたらどうなるのか‥‥など、想定外の操作をした時の「エラー対策」がなされていませんし、処理速度向上の試みも皆無です。

 また、もっと機能を拡張したいと言う要望もあるでしょう。「拡張子はpsd.jpgというみっともない表記ではなく、jpgだけの拡張子にしたい」「画像圧縮レベルを変更したい」「画像寸法を一律にリサイズしてから保存したい」「毎回保存場所を選択したい」‥‥など、いくらでも要望は出てくると思います。

 そうした改良案〜エラー対策も速度向上も機能拡張〜も、命令文作成者=プログラマの努力次第で充分可能です。さらに命令を書き加えるだけ、です。

 一例として、MacOSXが「素(す)の状態で利用可能な」画像ファイル処理の機能一覧を挙げてみましょう。

◆MacOSX10.4 sipsコマンド|オプション一覧


						
  Profile query functions: 
    -g, --getProperty key 
    -X, --extractTag tag tagFile 
    -v, --verify 
  Image query functions: 
    -g, --getProperty key 
    -x, --extractProfile profile 
  Profile modification functions: 
    -s, --setProperty key value 
    -d, --deleteProperty key 
        --deleteTag tag 
        --copyTag srcTag dstTag 
        --loadTag tag tagFile 
        --repair 
  Image modification functions: 
    -s, --setProperty key value 
    -d, --deleteProperty key 
    -e, --embedProfile profile 
    -E, --embedProfileIfNone profile 
    -m, --matchTo profile 
    -M, --matchToWithIntent profile intent 
    -r, --rotate degreesCW 
    -f, --flip horizontal|vertical 
    -c, --cropToHeightWidth pixelsH pixelsW 
    -p, --padToHeightWidth pixelsH pixelsW 
    -z, --resampleHeightWidth pixelsH pixelsW 
        --resampleWidth pixelsW 
        --resampleHeight pixelsH 
    -Z, --resampleHeightWidthMax pixelsWH 
    -i, --addIcon 

  Special property keys: 
    all                  binary data
    allxml               binary data
  Image property keys: 
    dpiHeight            float
    dpiWidth             float
    pixelHeight          integer (read-only)
    pixelWidth           integer (read-only)
    typeIdentifier       string (read-only)
    format               string jpeg | tiff | png | gif | jp2 | pict | bmp | qtif | psd | sgi | tga
    formatOptions        string default | [low|normal|high|best|<percent>] | [lzw|packbits]
    space                string (read-only)
    samplesPerPixel      integer (read-only)
    bitsPerSample        integer (read-only)
    creation             string (read-only)
    make                 string
    model                string
    software             string (read-only)
    description          string
    copyright            string
    artist               string
    profile              binary data
    hasAlpha             boolean (read-only)
  Profile property keys: 
    description          utf8 string
    size                 integer (read-only)
    cmm                  string
    version              string
    class                string (read-only)
    space                string (read-only)
    pcs                  string (read-only)
    creation             string
    platform             string
    quality              string normal | draft | best
    deviceManufacturer   string
    deviceModel          integer
    deviceAttributes0    integer
    deviceAttributes1    integer
    renderingIntent      string perceptual | relative | satuation | absolute
    creator              string
    copyright            string
    md5                  string (read-only)


 些細な画像変換をするにも市販の画像処理アプリケーションが無いと処理出来ない‥‥と思っていたとしたら、それは単にコンピュータの「そこぢから(底力)」を知らなかっただけです。コンピュータをポケ〜っとさせないで、もっと自分の思い通りにグリグリと活用しましょう!




INDEX


IntegerQ INDEX
h.ezura / afx / ezqnet