スクリーンショットを最高の1枚に仕上げるサービス

前回以降もshadowコマンドは修正を重ね、現在の仕様は以下のように落ち着いてきた。

Usage: shadow [-a ALPAH_VALUE(0-1)] [-b BLUR_RADIUS(0<=)] [-s SUFFIX] [-owh] [FILE ...]
  -a ALPAH_VALUE    影の透明度(0 <= ALPAH_VALUE <= 1 の少数値、デフォルト=0.5)
  -b BLUR_RADIUS    影のぼけ具合(0 <= BLUR_RADIUS の整数値、デフォルト=8)
  -s 'SUFFIX'       出力画像のファイル名に付加する文字列
  -o                輪郭なし
  -w                同じ画像ファイルに上書きする
  -h                このヘルプを表示する

Example:
  shadow test.png             ->  Default shadow(= shadow -a0.5 -b8 test.png)
  shadow -b4 test.png         ->  Nano shadow
  shadow -b2 test.png         ->  Line shadow
  shadow -b0 -a0 test.png     ->  None shadow
  shadow -b56 -a0.8 test.png  ->  OS X shadow
  shadow test.png -s '-nano'  ->  Output file name is 'test-nano.png'.
  shadow test.png -w          ->  Output file name is 'test.png' .(original is over written)

準備は整った。このshadowコマンドも「イメージを圧縮する」サービスに組み込んで、ブログ用のスクリーンショットを最高の一枚に仕上げるサービスにしてみようと思う。

準備すること

第一階層のサービスにする
  • 二本指クリックのサービスメニューが第二階層になってしまうと、その操作性は著しく低下する。(と感じている)
  • だから、常に第一階層のサービスメニューとして表示されるように、以下のおまじないをしておくのである。
  • OSXデフォルトでは4つ以上サービスが登録されていると、二本指クリックのコンテクストメニューに「サービス」という項目が作成されて、2階層目にサービスが展開されてしまう。
  • 自分にとってサービスメニューはよく使う操作なので、2階層目のメニューを選択するのは、マウスの軌跡を階段状に移動させなくてはならず、煩わしい...。

そこで...

# 99項目を超えるとサブメニューになる(99項目まで第一階層に表示される)
$ defaults write -g NSServicesMinimumItemCountForContextSubmenu -int 99
# 4項目を超えるとサブメニューになる(デフォルト)
$ defaults delete -g NSServicesMinimumItemCountForContextSubmenu


99項目まで第一階層の設定をした。

メッセージ通知
  • terminal-notifierを利用して、イメージの圧縮が完了したら通知するのだ。
  • 何も通知しないと、ちゃんと圧縮できたのかどうか不安になってしまう...。
  • リアクションは大事。
  • イメージを圧縮する.workflowは、terminal-notifierを利用して、メッセージを通知する。
  • terminal-notifierをインストルしておいた方が、より満足度の高いサービスとなるのだ。


terminal-notifierをインストールした。

画像圧縮ツール
  • Retina対応の画像は単純に考えただけで、そのままでは4倍(=縦横2倍)のファイルサイズになる。
  • Retina以前と同じように軽快に表示される環境を維持するためには、何らかの圧縮をするしかない。
  • 圧縮と品質はトレードオフの関係だが、自分の使い方では多くの場合、以下のツールを使う限りそれほど気にならない。
  • 但し、記事の内容によっては、どうしても高品質を維持したい時もある。そんな時だけは、圧縮するのをやめれば良い。


ImageAlphaとImageOptimをインストールした。

影の調整
  • 無駄に広すぎる影の領域を削除するということは、画像サイズを小さくすることにもなるので、高品質を保ったまま圧縮することと同じである。(素晴らしい)
  • 但し、影がまったくない画像というのもなんだかメリハリのない印象になってしまうので、必要最小限の影を残しておいた方が良さそう。


shadowコマンドを開発&インストールした。

スクリーンショットを影なしで撮影
  • shadowコマンドは影付き画像の影も調整できるのだが、その過程で影が混ざる部分のアンチエイリアス処理を削除している。
    • ウィンドウコーナー部分の若干の画質低下が今のところ避けられない。(人によっては気にならないかもしれないが)
  • 一方、影なしの画像に対しては、いったん影を削除する処理を省略できるので、高品質なアンチエイリアスを維持したまま、好みの影に調整できる。
  • OSXデフォルトでもoptionキーを押しながら撮影すれば影なしのスクリーンショットが撮れるのだが、押し忘れや面倒臭さがある。
  • よって、ウィンドウのスクリーンショット(command-shift-4、space)を常に影なしで撮影する設定にしてしまう。
# ウィンドウのスクリーンショットを常に影なしで撮影する
$ defaults write com.apple.screencapture com.apple.screencapture disable-shadow -bool yes
$ killall SystemUIServer

# ウィンドウのスクリーンショットを影ありで撮影する(デフォルトに戻す)
$ defaults delete com.apple.screencapture disable-shadow
$ killall SystemUIServer


常に影なしで撮影する設定にした。

サービスにまとめる

基本的に前回作った「イメージを圧縮する」サービスにshadowコマンドを組み込んだだけなのだが、以下の部分で若干手こずった。

shadowコマンドは二回使う
  • 基本的に影なしの画像を圧縮するはずなのだが、稀に影ありの画像を扱いたいこともあるかもしれない。
  • 影ありのまま画像サイズを指定してしまうと、影のない部分の画像が目指すサイズよりも小さくなってしまう...。
  • 画像サイズを指定する前に影の調整をしてしまうと、画像とともに影も縮小されて、影のイメージも変化してしまう...。
  • そこで、画像サイズを指定する前に、shadowコマンドで影の部分を削除した画像を生成して、その画像に対してサイズを指定している。
  • そしてリサイズ後、もう一度shadowコマンドを実行して、好みの影に調整している。
  • その後、二つの画像圧縮をして、できる限りコンパクトな画像に仕上げている。
    • ちなみに、処理の最後に影を付けると、せっかく圧縮した画像が馬鹿でかいサイズに戻ってしまった。
    • きっと影付けの過程でNSImageに再描画しているので、事前の圧縮が台無しになってしまうのだと思う。

20121119162646

AppleScriptシェルスクリプトが混在する時の注意

今回のようにAutomatorAppleScriptシェルスクリプトを混在させて使う時は、次のアクションに渡すファイルパスの形式に注意しておく必要があった。

  • 同じファイルを示すパスを渡しているのに、形式の違いによって動かなくなってしまうのは著しく不便である。
  • そこで、AppleScript側にもどちらの形式のパスを受け取ってもエイリアスに変換してから処理する仕組みを追加した。


on run {input, parameters} set input to alias_list(input) (* input = {alias, alias, ...} *)
--以下に必要な処理を続ける
end run

on alias_list(fs) set a_list to {} repeat with f in fs
set a_list's end to _alias(f) end repeat
a_list
end alias_list

on _alias(f) try
tell application "Finder" to (f as alias) on error
tell application "Finder" to (f as POSIX file as alias) end try
end _alias

これで、パスの形式の違いによるエラーで悩む必要がなくなった!

シュルスクリプトでファイルパスを連携させる方法
  • Automatorのサービスとは、選択されたファイルパスあるいはテキストを引数として、ワークフローを実行する仕組みである。
  • 今回のように画像ファイルに対して複数の処理を繰り返す時には、同じファイルパスを次のアクションにも渡してあげる必要がある。
  • AppleScriptなら「input」で受け取ったものを「return input」するだけで用が足りる。
  • では、シェルスクリプトではどうすべきなのだろう?
    • echo "$@"では一つの連続した引数となってしまった。
  • ならばループを回して、必要な回数分echoするようにしてみた。
for f in "$@"; do		echo "$f";		done

これで次のアクションにも確実にファイルパスのリストが渡せた!

サービスをテストする方法
  • サービスは2本指タップから起動できてとても便利なのだけど、開発中にこの方法で起動してテストしても、エラーが発生した箇所が分からなくて困る。
  • エラー箇所を特定するためには、ワークフロー書類の実行ボタンを押して起動する必要があるのだけど、
  • サービスの場合、引数となるファイルを指定する必要があるので、そのままではエラーになってしまう。
  • そんな時は、最初のアクションに「選択されたFinder項目を取得」アクションを追加すればいい。

20121120085259

  • これで、そのとき最前面のFinderウィンドウで選択されているファイルを対象にワークフローを実行できるのだ。

ちなみに、

  • テキストを選択して起動するサービスの場合も同じ。「指定されたテキストを取得」アクションを追加するのだ。
      • 注意:このアクションの場合は、選択する予定のテキストを入力しておく必要がある。

20121120085225

ダウンロード


写真を撮ることは好きだ。今の時期なら、今にも落葉しそうな紅葉した葉っぱと、秋晴れの空の青とのコントラストがじつに感動的に見える。スクリーンショットも同じ。コンパクトな影でキリリと引き締まったスクリーンショットが並んでいると、なんだか嬉しくなるのだ!

      • ところで、前から気になっていたのだけど、画像の下に紫のラインがなぜか表示されてしまう...。
      • これはcssの設定を弄れば非表示にできるのだろうか?一体どうやって削除したら良いのだろう?