見出し画像

重い腰を上げてAppleScriptを触り始めた ~Pixelmator 自動化~

こんばんは。

僕はなんだかんだ長いことMacを使っています。

初めはMacBookを開くたびに背面へ回り、リンゴマークを眺めて満足するベイビーでした。そこから色々なアプリケーションを操作してMacを操るキッズとなり、匿名掲示板で煽られたことがきっかけで泣きながらターミナルと向き合うUNIXボーイとなり、とうとう今ではブラウザを開いてエロ漫画サイトを徘徊するだけのキモオタに成り果てました。もはやコンピューティングのコの字もありません。

そんなこんなでMacユーザとして順当に成長してきた僕ですが、どうしても避けていたものがあります。それがズバリ「AppleScript」です。

UNIXコマンドについては割と楽しんで覚えられました。今ではそれなりにシェルスクリプトも書けますし、ある程度の処理ならCLIでどうにかできます。

プログラミングを学習し始めたときも拒否反応のようなものは一切ありませんでした。今ではPythonを使って日々エロ画像を収集するクローラの開発をしています。

にも関わらず、AppleScriptだけはど〜〜〜〜しても触る気が起きません。しかも、「そこまで嫌なら使わなきゃいいのでは?」と訊かれて素直に「うん」と答えられないのが痛いところです。何故なら、AppleScriptを使いこなせば僕の日常的に発生するタスクを効率的に処理できるという事実に気づいてしまっているからです。

幸いにも、今は時間が有り余っているために過去に少し触っただけのnimやelectronの学習をしているところだったので、これは好機だと考えついでにAppleScriptの勉強も真面目に取り組んでみようと思い立った次第です。

とりあえず、今回僕が処理したかったタスクと、それをこなすためのAppleScriptを紹介して、AppleScriptを学ぶメリットを伝えられたらなと思います。

本題の前に

そもそも何故AppleScriptを触りたくなかったのかを述べさせてください。

まず、一般的な汎用高級プログラミング言語は以下の様な構文です。

def async_download(self, absolute_path: str = None, directory_name: str = None, start: int = 1, end: int = None):
       dir_path = absolute_path
       dir_name = directory_name

       if not dir_path:
           dir_path = './'

       if not dir_name:
           dir_name = self.title if not self.title else str(random.randrange(10**6, 10**7))

      #中略
      
       for i, item in enumerate(self._image_list):
           if i + 1 < start:
               continue
           if i + 1 > end:
               break
           do_download_img.append(item)

こちらはPython言語で書かれたプログラムです。処理の内容は特に解説しませんが、恐らくPython言語以外でもCやJavaなど何かしらのプログラミング言語を学んだ経験のある方であれば、パッと見てどのような処理をしているのかは直ぐに読めると思います。

というのも、殆どのプログラミング言語には共通する命令文や機能が多く存在するからです。例えば、分岐処理を行うif文や、反復処理を行うfor,while文など。そして、手続き型プログラミングやオブジェクト指向言語であれば、値に対して何らかの処理を行う場合、関数やメソッドを呼び出します。

次にAppleScriptを見てみましょう。

tell application "Pixelmator Pro"
    set targetImg to choose file with prompt "対象の画像を選択してください" of type {"public.image"} with multiple selections allowed
    set outputDir to choose folder with prompt "出力先のディレクトリを選択してください"
    repeat with i from 1 to number of targetImg
    
        set currentImg to open item i of targetImg

		trim canvas currentImg mode transparency

		export for web currentImg to file ((outputDir as text) & name of currentImg & "-trim" & ".png") as PNG with properties {advanced compression:true, keep transparency:true}

		close currentImg without saving

    end repeat
end tell

こちらの処理の内容は後ほど解説しますが、パッと見て何をしているのか全くと言って良いほどわかりません。AppleScriptは自然言語にかなり近いので、プログラミングの知識がなくても、英語が読める人であれば雰囲気は把握できるのかもしれませんね。

そう、AppleScriptはプログラミング言語の知識を応用できないのです。もちろんプログラミング的思考は大いに活用できますが、それをコードへアウトプットすることがスムーズに行きません。

つまり、僕の感覚として、一般的なプログラムを記述するときは
「プログラミング的思考 + 制御構文 + 言語の独自仕様」
が知識として必要となりますが、AppleScriptは
「プログラミング的思考 + 英語 + 言語の独自仕様」
となるのです。

変数の宣言が

set 変数名 to 内容

だったり、配列の要素数を取得するのが

number of 配列
---もしくは
count of 配列

だったり、整数型を文字列型へキャストするのが

変数 as text
---もしくは
変数 as string

だったりと、もうとにかく英語。

Pythonなら

#変数の宣言
変数名 = 内容

#配列の要素数
len(変数名)

#整数型から文字列型へキャスト
str(変数名)

ですからね。長々と語りましたが、つまるところ僕が英語弱者だから苦手意識があるというだけの話です。

本題・何がしたかったのか

ようやく本題です。最初に、僕が何をしたかったのかを説明させてください。

まず、僕の手元に以下のようなPNG画像が100枚あります。

名称未設定

これは透過PNGになっているのですが、女性が全体より左側に位置しているのがおわかりですか。これでは無駄なスペースが多いので、以下のように余白を無くした状態へ処理したいと考えました。

名称未設定2


以上の処理を行う方法として、自身でトリミングを行うのはファイルの数が多いので却下しました。また、全ての画像で被写体が左側へ寄ってくれていれば、ImageMagickを用いて一括で指定したサイズへトリミングするシェルスクリプトを書いたのですが、今回は残念ながらどの位置に被写体が居るのかランダムだったので、それも却下です。

トリミング自体も、目視で行うのではなく、透明部分をギリギリまで削りるのが理想です。なので、Mac用のペイントソフトであるPixelmator Proを使用することにしました。

スクリーンショット 2021-11-29 7.26.44

まずこのように画像を開きます。次にメニューバーの「イメージ」→「キャンバスのトリミング」を選択すると、以下のように自動的にトリミングを行なってくれます。

スクリーンショット 2021-11-29 7.26.49

最後に、トリミングし終えた画像を「Web用に書き出す」で透明度を保ったままPNG画像で書き出して終了です。

スクリーンショット 2021-11-29 7.27.04

さて、じゃあこれを一枚一枚処理して残り98枚……ってやってられるか〜〜〜!😡😡😡

というわけでAppleScriptの出番です。

本題・AppleScriptでPixelmatorを自動化しよう

そもそもの話、Pixelmator ProがAppleScriptに対応しているのかを知る必要があります。Pixelmator Pro.appをスクリプトエディタへ投げると、以下のような辞書が表示されました。

スクリーンショット 2021-11-29 7.35.27

どうやらしっかりと対応しているようです。では、まずはアプリを呼び出す処理を書いていきましょう。

tell application "Pixelmator Pro"
---ここに処理を書く
end tell

tell文はアプリケーションを呼び出します。次に、今回はPixelmatorで開かれている作業中のドキュメントではなく、PNG画像ファイルに処理をすればよいわけですから、まずはそのファイルを指定してあげます。

tell application "Pixelmator Pro"
    set targetImg to choose file with prompt "対象の画像を選択してください" of type {"public.image"} with multiple selections allowed
end tell

追加した処理について見ていきます。まず、set文は変数宣言です。targetImgという変数にchoose fileコマンドの返り値を代入しています。
choose fileコマンドについての説明を見てみます。

Apple公式のヘルプを見ると、パラメータと返り値について書かれています。とは言っても、返り値は「ユーザが指定したアプリケーションへの参照を返します。」というよくわからないものなので、display dialogコマンドを使って中身を確認してみたほうが良いです。結論から言うと、単体のファイルを選択した場合はそのファイルへの参照が、複数のファイルの場合は配列として返却されます。今回はmultiple selections allowedパラメータをtrueにしているので、複数のファイルの選択を想定していますから、targetImg変数は配列だと考えてください。

ちなみにAppleのヘルプより以下のサイト様のほうが見やすいです。

さて、処理すべき画像ファイルを入力したら、次は出力先のディレクトリを指定することにします。

tell application "Pixelmator Pro"
    set targetImg to choose file with prompt "対象の画像を選択してください" of type {"public.image"} with multiple selections allowed
    set outputDir to choose folder with prompt "出力先のディレクトリを選択してください"
end tell

choose folderコマンドのパラメータもchoose fileコマンドとほぼ同等です。つまり、multiple selections allowedパラメータを指定していないので、デフォルト値のfalseとなります。よって、outputDir変数は単一のディレクトリを参照しています。

お膳立てはこれで終わりです。あとは画像を一枚一枚ループして、処理を行えば良いだけとなります。

多くのプログラミング言語では繰り返し処理を行う制御文にfor文やwhile文が使われていますが、AppleScriptではrepeat文です。更に、繰り返し時の処理はwithで指定します。ちなみにwhileが存在しないのかと言うと、そういうわけでもありません。条件式に論理値を指定する場合はwhileを使用します。(多分)

tell application "Pixelmator Pro"
    set targetImg to choose file with prompt "対象の画像を選択してください" of type {"public.image"} with multiple selections allowed
    set outputDir to choose folder with prompt "出力先のディレクトリを選択してください"
    repeat with i from 1 to number of targetImg
        ---ここにループ内の処理を書く
    end repeat
end tell

条件式がちょっと分かりづらいですね。withの後にカウンタ変数iを宣言しています。また繰り返し処理を行う回数として、1から"number of targetImg"、つまり1から"targetImg配列の要素数"まで繰り返すことを宣言しています。

tell application "Pixelmator Pro"
    set targetImg to choose file with prompt "対象の画像を選択してください" of type {"public.image"} with multiple selections allowed
    set outputDir to choose folder with prompt "出力先のディレクトリを選択してください"
    repeat with i from 1 to number of targetImg
    
        set currentImg to open item i of targetImg

		trim canvas currentImg mode transparency

		export for web currentImg to file ((outputDir as text) & name of currentImg & "-trim" & ".png") as PNG with properties {advanced compression:true, keep transparency:true}

		close currentImg without saving

    end repeat
end tell

はい、一気にループ内の処理を書きました。一つずつ何をしているのか確認します。

まず、「set currentImg to open item i of targetImg」
の部分は単なる変数宣言です。openコマンドは単純にファイルを開くためのものです。「item i of targetImg」は、targetImg配列のi番目の要素を取り出す処理です。iはカウンタ変数なので、配列の一番目の要素からn番目の要素がループするたびに順番に開かれるようになっています。

「trim canvas currentImg mode transparency」
については、Pixelmator Pro固有のコマンドとなります。

画像7

辞書を見てみると、複数のmodeが指定できるのがわかります。transparency/‌top left color/‌bottom right colorの三種類ですが、これらは英語で書かれているだけで、以下の選択肢と同じ意味です。

スクリーンショット 2021-11-29 7.26.49

僕が選択したいのは「透明なピクセル」なので、modeにはtransparencyを指定しています。

「export for web currentImg to file ((outputDir as text) & name of currentImg & "-trim" & ".png") as PNG with properties {advanced compression:true, keep transparency:true}」
これは長いので分割して見ていきます。

「export for web currentImg」
これもPixelmator Pro固有のコマンドで、GUIで「Web用に書き出し」を選択するのと同様の機能です。export for webコマンドに現在開いているcurrentImgを指定しています。ちなみにWeb用ではなく、普通の書き出しも可能です。

画像9

「to file ((outputDir as text) & name of currentImg & "-trim" & ".png")」
これは書き出す先のファイルを指定する部分です。(outputDir as text)で二行目に指定している出力先のディレクトリを文字列へ変換しています。そして&を使って文字列結合を行っています。name of currentImgは現在開いている画像のファイル名です。その先の"-trim"と".png"は単なる文字列。

「as PNG」
ドキュメントにあるように、asの後にPNG,JPEG,GIF,SVG,WebPのいずれかを指定します。

「with properties {advanced compression:true, keep transparency:true}」
こちらもPixelmator ProのGUIを見ると、対応している機能がわかります。

画像10

今回僕が指定したいのは「高度な圧縮を使用」「透明度を維持」です。ではこれに対応するプロパティはどれでしょうか。辞書を見てみます。

画像11

「高度な圧縮を使用」「advanced compression (boolean)」
「透明度を維持」「keep transparency (boolean) 」
ですね。どちらもbooleanなので真偽値を指定すれば良いです。

「close currentImg without saving」
現在開いている画像を閉じます。without savingは保存するかの確認をしないようにするものです。

以上!

おわりに

一つ一つ噛み砕いて行くと大して難しいものではないですが、やはりまだ直感的にスラスラっと処理を書ける程には至っていません。当分はドキュメントと辞書とにらめっこしながらとなりそうですね。

参考文献
http://tonbi.jp/AppleScript/Introduction/08/
http://tonbi.jp/AppleScript/Dictionary/OSAX/
https://mc909j.blogspot.com/p/document.html
https://www.pixelmator.com/tutorials/resources/advanced-automation-and-scripting-with-applescript/
https://www.pixelmator.com/community/viewtopic.php?t=18001

この記事が気に入ったらサポートをしてみませんか?