Archive for 2月, 2017

2017/02/27 指定Bundle IDのプロセス存在確認(ASOC)v3

Bundle IDで指定したアプリケーションプロセスが起動中かどうかを調べるAppleScriptです。

よく書いては書き捨てるレベルの、素朴な機能のScriptです。Cocoaの機能を呼び出すAppleScriptObjCにだんだん書きなれてきて、

 ・Cocoaの機能を積極的に利用する
 ・ループや条件判断を利用しないで書く
 ・Cocoaらしい記述を利用して書く

という記述方法がわかってきたような気がします(わかった、とは言いません)。

 ・指定Bundle IDのプロセス存在確認(ASOC)(v1)
 ・起動中のプロセスの存在確認(ASOC)v2
 

AppleScript名:指定Bundle IDのプロセス存在確認(ASOC)v3
– Created 2015-02-08 by Takaaki Naganoya
– Created 2017-02-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
–http://piyocast.com/as/archives/4486

set aRes to chkAppProcesByBundleID("com.apple.iWork.Keynote")
–> true

on chkAppProcesByBundleID(aBundleID as string)
  set procArray to current application’s NSWorkspace’s sharedWorkspace’s runningApplications()’s valueForKeyPath:"bundleIdentifier"
  
return (procArray’s containsObject:aBundleID) as boolean
end chkAppProcesByBundleID

★Click Here to Open This Script 

2017/02/26 PDFViewからscroll barを消す

PDFViewからscroll barを消すAppleScriptの解説です。

pdfview_scroller.png

MOSAの勉強会(BUKURO.swift)で開発者の皆さんにご意見をいただき、帰宅後に実験してできました。

◼Hide scroll bar from PDFView
https://www.slideshare.net/Piyomaru/hide-pdf-views-scrollbar

2017/02/25 技術書典2で新刊書籍「iTunes Control」を頒布します

ぴよまるソフトウェアは、4月9日に秋葉原アキバ・スクウェアで開催される「技術書典2」(え-11)に出展。新刊電子書籍を販売します。

tbf2.png

新刊のタイトルは「iTunes Control」。iTunes+AppleScriptにおける「Hello World」体験を提供する本で、誰にでもわかり、誰にでもその実態を経験できるというもの。難易度も急に上がったりしません(Hop, Step, Jumpの3段階)。

book2.png

book3.png

book4.png

edama2さんに1か月前ぐらいに見せたら「前作からかわりすぎ!!!」と驚かれたので、「頭どうかしちゃったの?」「別人か?!」とかいうショックをやわらげるためにここに表紙その他を掲載しておきます(^ー^;;。

ブースではQRコードで書籍の販売を行い、販売したQRコードをスマホで読み取って販売元にメール送信すると、クラウドストレージにアップロードされた書籍のURLがメールで返送されてきます。

そして、この販売システム自体がAppleScriptで作ってあり、Mac上で稼働しているのです。

2017/02/23 NSSharingService経由でメールを新規作成(作成するだけ、送信しない)

NSSharingServiceを用いて新規メール作成を行うAppleScriptです。

単に指定のSubject、本文、送信先アドレスのメールを、OS標準のメーラー(default mailer)で新規作成するだけです。送信しません(AppleScript使いに見せると激怒されるレベル)。単なるCocoaの機能テストなので全力で見逃してください。

AppleScript名:NSSharingService経由でメールを新規作成(作成するだけ、送信しない)
– Created 2017-02-23 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4478

set shareItems to {“てすとだよおおおん”}
set aService to current application’s NSSharingService’s sharingServiceNamed:(current application’s NSSharingServiceNameComposeEmail)
aService’s setDelegate:me
aService’s setRecipients:{“maro@piyocast.com”}
aService’s setSubject:“試験送信”
aService’s performWithItems:shareItems

★Click Here to Open This Script 

2017/02/23 Myriad Tables v1.0.7がリリースされる

Shane StanleyによるAppleScript Libraries「Myriad Tables Lib」のバージョン1.0.7がリリースされました。AppleScriptから手軽に「表」インタフェースを利用可能にするものです。

table3.png

同ライブラリをサイトからダウンロードし、~/Libraries/Script Librariesフォルダに入れるとAppleScriptから使えるようになります(このフォルダが存在しない場合には作成)。

tables01.png

tables02.png

同ライブラリにはAppleScript用語辞書が用意されており、Script Editorに直接ドラッグ&ドロップではなく、いったんScript Editorの「ライブラリ」ウィンドウに登録し、リスト上の「Myriad Tables Lib」を選択した状態で「用語説明を開く」をクリックすると、用語辞書の内容を確認できます。

tables003.png

tables004_resized.png

一般的に、Excel書類やCSVなどで支給されたデータに対して、どの行のデータを処理するかをセルの選択範囲で明示的にScriptに対して指示することはよくあります。ただ、実行環境にMicrosoft Excelがすべてインストールされているわけでもありません。

Myriad Tables Libがあれば、CSVデータ中の処理範囲を選択したり、簡易的なデータ入力・確認用のGUIをAppleScriptから利用できます。

Myriad Tables Lib 1.0.7の新機能として紹介されているものを、同ライブラリ添付のサンプルコードから紹介してみましょう。

新機能:セルのダブルクリックを「OK」ボタンのクリックと等価とみなす「double click means OK」オプション

table1.png

AppleScript名:double click means OK
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
use script “Myriad Tables Lib” version “1.0.7″

– This table shows how to use the ’double click means OK’ parameter
display table with data {“One”, “Two”, “Three”, “Four”, “Five”} with title “Simple table” with prompt “You can double-click an entry rather than selecting and pressing OK” with double click means OK and empty selection allowed

–> {rows selected:{5}, values selected:{”Five”}, values returned:{”One”, “Two”, “Three”, “Four”, “Five”}, button number:1, timed out:false, final position:{978.0, 254.0, 246.0, 278.0}}

★Click Here to Open This Script 

新機能:ダイアログの表示座標を指定する「initial position」オプション

table2.png

AppleScript名:initial position
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
use script “Myriad Tables Lib” version “1.0.7″

– This table shows the use of the ’initial position’ parameter to set the dialog’s position
set theTable to make new table with data {{1.0, 1, 1.11, 1}, {-2.0, 2, 2.2, 2}, {3.11, -3, 3.11, 3}, {4.0, 4, -4.41, 4}, {5.0, 5, 5.55, -5}} column headings {“Reals”, “Integers”, “Reals”, “Integers”} with prompt “This table appears at the top-left of the screen” with double click means OK and empty selection allowed
modify table theTable initial position {0, 0} with alternate backgrounds
display table theTable

★Click Here to Open This Script 

initial positionでは画面上の表示座標だけでなく、サイズ{表示位置(X), 表示位置(Y), 表示幅(X),表示高さ(Y)}の指定も可能です。

AppleScript名:initial position_2
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
use script “Myriad Tables Lib” version “1.0.7″

– This table shows the use of the ’initial position’ parameter to set the dialog’s size and position
set theTable to make new table with data {{1.0, 1, 1.11, 1}, {-2.0, 2, 2.2, 2}, {3.11, -3, 3.11, 3}, {4.0, 4, -4.41, 4}, {5.0, 5, 5.55, -5}} column headings {“Reals”, “Integers”, “Reals”, “Integers”} with prompt “This table’s size and position have been set in the script” with double click means OK and empty selection allowed
modify table theTable initial position {100, 10, 400, 400} with alternate backgrounds
display table theTable

★Click Here to Open This Script 

新機能:ダイアログの前回表示位置&サイズを取得する「final position」

AppleScript名:final position
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
use script “Myriad Tables Lib” version “1.0.7″

– This table shows how to retrieve the final position and size of the dialog
set theTable to make new table with data {{1.0, 1, 1.11, 1}, {-2.0, 2, 2.2, 2}, {3.11, -3, 3.11, 3}, {4.0, 4, -4.41, 4}, {5.0, 5, 5.55, -5}} column headings {“Reals”, “Integers”, “Reals”, “Integers”} with prompt “Move and resize this dialog before clicking OK” with double click means OK and empty selection allowed
modify table theTable with alternate backgrounds
set theResult to display table theTable
set theBounds to final position of theResult

– This table is positioned using the results from the previous one
set theTable to make new table with data {{1.0, 1, 1.11, 1}, {-2.0, 2, 2.2, 2}, {3.11, -3, 3.11, 3}, {4.0, 4, -4.41, 4}, {5.0, 5, 5.55, -5}} column headings {“Reals”, “Integers”, “Reals”, “Integers”} with prompt “This table’s size and position should match those used last time” with double click means OK and empty selection allowed
modify table theTable initial position theBounds with alternate backgrounds
display table theTable

★Click Here to Open This Script 

2017/02/23 リストから重複要素のみを返す v2

Shane Stanleyによる「リストから重複する要素のみを抽出して返すAppleScript」の改良版です。シンプルで美しい処理になっています。

この手の処理でNSSetやNSCountedSetを使うのは「そういうものだろー」とすぐに思いつきますが、「minusSet:」というのはドキュメント中から見つけ切れませんでした。これは深い(^ー^;;。

また、ループ処理や条件判断の処理が入っていないため、自分の書いたバージョンよりも2.3倍ぐらい高速でした(100回実行時の平均)。

AppleScript名:リストから重複要素のみを返す v2
– Created 2017-02-23 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4469

set aList to {“4efa7f9f587f3d1dbc4a1f6ebff92ef5″, “59cd07ea69acc2c9004dc815803cf184″, “5841f4bcc13e85c3b8ba1eafcacd43be”, “dfb393cd3c0eb3f228236367f171cd01″, “59cd07ea69acc2c9004dc815803cf184″, “59cd07ea69acc2c9004dc815803cf184″}
set dList to returnDuplicatesOnly(aList) of me
–> {”59cd07ea69acc2c9004dc815803cf184″}

on returnDuplicatesOnly(aList)
  set countedSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set simpleSet to current application’s NSSet’s setWithArray:aList
  
countedSet’s minusSet:simpleSet
  
return countedSet’s allObjects() as list
end returnDuplicatesOnly

★Click Here to Open This Script 

2017/02/22 リストから重複要素のみを返す

リストから重複する要素のみを抽出して返すAppleScriptです。

Pure AppleScriptでは単純ループでひたすら「is in」演算子を使って求める処理ですが、リストの要素数が増えた場合(数万項目とか)でも速度が低下しないようにCocoaの機能を使って処理してみました。

AppleScript名:リストから重複要素のみを返す
– Created 2017-02-22 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4468

set aList to {“4efa7f9f587f3d1dbc4a1f6ebff92ef5″, “59cd07ea69acc2c9004dc815803cf184″, “5841f4bcc13e85c3b8ba1eafcacd43be”, “dfb393cd3c0eb3f228236367f171cd01″, “59cd07ea69acc2c9004dc815803cf184″, “59cd07ea69acc2c9004dc815803cf184″}
set dList to returnDuplicatesOnly(aList) of me
–> {”59cd07ea69acc2c9004dc815803cf184″}

–リストから重複要素のみを返す
on returnDuplicatesOnly(aList)
  set aSet to current application’s NSCountedSet’s |set|()
  
aSet’s addObjectsFromArray:aList
  
  
set theEnumerator to aSet’s objectEnumerator()
  
set anArray to current application’s NSMutableArray’s alloc()’s init()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
set aCount to (aSet’s countForObject:aValue) as integer
    
if aCount > 1 then
      anArray’s addObject:aValue
    end if
  end repeat
  
  
return anArray as list
end returnDuplicatesOnly

★Click Here to Open This Script 

2017/02/21 NSDataからMD5値を計算する

NSDataからMD5のdigest値を計算するAppleScriptです。オープンソースのプロジェクト「NSData-MD5」(By Francis Chong)を利用しています

MD5のdigest計算はたいていファイルからshell commandなどで求めるのが一般的な処理ですが、いったんファイル化しなくてはならないので、扱いに困るケースもあります。

そこで、MD5をデータ(変数に入れたデータ)から直接求めるObjective-Cのプログラム(NSData+MD5)を見つけて、Cocoa Framework化し、AppleScriptから呼べるようにしてみました。

テストのためには本フレームワーク(本当にただXcodeのプロジェクトを作ってObjective-Cのプログラムを放り込んだだけ)を~/Library/Frameworks/フォルダに入れて、本AppleScriptを実行してみてください(自己責任でおためしください)。

→ Download Framework

データをMD5 digest化して保持しておけると、大容量データの扱いがとても便利(容量を取らない。相互に照合はできる)なので、この手の機能は欠かせません。ファイルを経由せずに計算したいと考えるのもとてもまっとうな話で、Googleで検索してもよく実装例を見かけます。

MD5値の計算のためにはNSDataに変換する必要があり、手元ではNSStringとNSImage(bitmap化してNSData化)については計算できています。共感していただける人が少ないことはわかっていますが、自分的にはかなり重要な機能です(^ー^;;。

AppleScript名:NSDataからMD5値を計算する
– Created 2016-02-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “md5FromDataKit” –https://github.com/siuying/NSData-MD5
–http://piyocast.com/as/archives/4466

set aStr to “ぴよまるソフトウェア”
set aNSStr to current application’s NSString’s stringWithString:aStr
set aData to aNSStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
set aMD5Hex to (current application’s NSData’s MD5HexDigest:aData) as string
–>  ”2d0b4e205f274f20b17dc8ca4870f1db”

set aMD5 to (current application’s NSData’s MD5Digest:aData)
–>  (NSData) <2d0b4e20 5f274f20 b17dc8ca 4870f1db>

★Click Here to Open This Script 

2017/02/18 com.apple.quarantineがドロップレットに与える影響

macOS, Sierraになって、AppleScript Dropletの挙動が変わり、「Dropletにファイルをドラッグ&ドロップしてもドロップしたすべてのファイルが処理されるわけではない」という状況が確認されています。

そもそもの始まりは、DropletにDropしたファイルが本来1つのリストとして処理されるはずだが、複数のリストに分割されて処理されるというものでした。実行してみると、たしかにそのとおりで、デスクトップ上に転がっているファイルを処理してみても、Dropした個数が10個で、openハンドラに引き渡されるファイルは1つのlistに格納されて処理されることを期待するところですが、そうなっていませんでした。

この、「Dropされたファイルが1回で処理されずに複数のファイルオープンイベントとして処理される」問題の検証を行ってみたところ、さらに不思議な挙動が見えてきました。

一番確認しやすいのは、「ダウンロード」フォルダ中のファイルをDropletにDrag & Dropして個数をかぞえてみた場合です。

Dropしたファイルの数と、Dropletの中でカウントした受信ファイルの総数が合わないケースが確認されました。

本件についてAppleScript Users-ML上でいろいろ情報交換したところ、

(1)Dropletの処理に影響を与えているのは、ファイルの拡張属性(Xattr)の「com.apple.quarantine」という属性らしい

(2)OSのレベルでの挙動の変更により、「com.apple.quarantine」属性を持つファイル、つまり安全性が確認されていない(OS側から「ダウンロードされたファイルですが、オープンしてもよいですか?」という確認ダイアログでユーザーに承認されていない)ものについては、オープンされないようになっている模様

(3)AppleScriptのDropletだけでなく、一般的なmacOSアプリケーションでも同じ状況

なので、AppleScriptの処理系のバグということではない様子(macOS 10.11上とバージョン変わってないし)。とはいえ、その状況がユーザーにフィードバックされないため、「ドロップレットの挙動がおかしくなった」という風にしか見えないところ。

com.apple.quarantine属性を持つファイル(ダウンロードされたりメールに添付されて送られてきたりしたもの)を処理しないというのは仕方のないところかもしれませんが、確認したり状況を明示的に解除するためのインタフェースが用意されていないのは問題に見えます。

いくつか同属性を解除する方法は見つけていますが、本来セキュリティ維持のための仕組みであり、ユーザー側で対応を試みてもOS側で迂回されたりガードされる可能性もあります。

→ ASOCベースのDropletにおいて回避方法がわかってきました

個人的には、Dropletが好きではないのであんまりDropletを作らない(仕事でもDroplet処理は極力避けている)ことにしているため、問題に気づかなかったものです。

2017/02/16 YAMLのじっけん

オープンソースのフレームワーク「YAML.framework」(Mirek Rusin)を用いて、構造化データ記述言語であるYAMLとオブジェクト(listやrecord)との間の相互変換を行うAppleScriptです。

これまでYAMLとは縁のない生活を送ってきましたが、とくに問題はありませんでした。構造を持つデータの分量が増えた場合には、プログラム中に直接記述せず、Excelの表からデータを読み取って処理したり、データベースなど他のデータソースからデータを取得していました。

ただ、listとかrecordの内容が込み入ってくる(フィールド数が多いとか、入れ子構造の段数が深いとか)と、こういう仕組みがあったほうが便利なんだろうな、ということは理解できます。

YAML.frameworkのプロジェクトをXcodeでビルドし、出来上がったフレームワークを~/Library/Frameworksに入れてテストしてみてください。

途中、Githubに掲載されているObjective-CのサンプルプログラムがNSInputStreamを使っており、これをAppleScriptに書き換えて呼び出すとAppleScriptの処理系が100%クラッシュ。NSDataを経由して変換する方法なども試してみましたがNSInputStreamを作りにいくとクラッシュ。

結局、Stringから直接変換するメソッドがあったため、これを使用することで安定して処理できるようになりました。

当初は機械学習フレームワークの「YCML」をいじくっていたのですが、その過程で本フレームワークを発見。なかなか有用性が高そうなので試してみた次第です。

AppleScript名:YAMLのじっけん
– Created 2017-02-16 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “YAML” –https://github.com/mirek/YAML.framework
–http://piyocast.com/as/archives/4464

–YAMLの文字列からオブジェクトを生成する
set aYAMLstr to
items:
- name: Foo
- name: Bar

set aStr to current application’s NSString’s stringWithString:aYAMLstr
set aData to aStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)

set aData to current application’s YAMLSerialization’s objectsWithYAMLString:aStr options:(4096) |error|:(missing value)
–>  (NSArray) {{items:{{name:”Foo”}, {name:”Bar”}}}}

–オブジェクトからYAMLの文字列を取得する
set aString to (current application’s YAMLSerialization’s createYAMLStringWithObject:aData options:(1) |error|:(missing value)) as string
(* –>
“—
- items:
- name: Foo
- name: Bar
…”
*)

★Click Here to Open This Script 

AppleScript名:YAMLのじっけん4
– Created 2017-02-16 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “YAML” –https://github.com/mirek/YAML.framework
–http://piyocast.com/as/archives/4464

–YAMLの文字列からオブジェクトを生成する
set aYAMLstr to
- name: Smith
email: smith@mail.com
- name: Shelton
email: shelton@mail.com
- name: Kelly
email: kelly@mail.com

set aRes to retObjectFromYAMLString(aYAMLstr) of me
–>  {{{name:”Smith”, email:”smith@mail.com”}, {name:”Shelton”, email:”shelton@mail.com”}, {name:”Kelly”, email:”kelly@mail.com”}}}

set bYAMLstr to
names: [Smith, Shelton, Kelly]
emails: [smith@mail.com, shelton@mail.com, kelly@mail.com]

set bRes to retObjectFromYAMLString(bYAMLstr) of me
–>  {{names:{”Smith”, “Shelton”, “Kelly”}, emails:{”smith@mail.com”, “shelton@mail.com”, “kelly@mail.com”}}}

–オブジェクト(list)からYAMLの文字列を生成する
set bStr to retYAMLStringFromObject(bRes)
(*  
“—
- names:
- Smith
- Shelton
- Kelly
emails:
- smith@mail.com
- shelton@mail.com
- kelly@mail.com


*)

on retObjectFromYAMLString(aYAMLstr as string)
  set aStr to current application’s NSString’s stringWithString:aYAMLstr
  
set aData to aStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aData to current application’s YAMLSerialization’s objectsWithYAMLString:aStr options:(4096) |error|:(missing value)
  
return aData as list
end retObjectFromYAMLString

on retYAMLStringFromObject(anObject)
  set aString to (current application’s YAMLSerialization’s createYAMLStringWithObject:anObject options:(1) |error|:(missing value)) as string
  
return aString
end retYAMLStringFromObject

★Click Here to Open This Script 

2017/02/14 指定バンドルIDのアプリケーションのアイコンへのパスを求める

指定バンドルIDのアプリケーションのアイコンへのパスを求めるAppleScriptです。

とりあえず、ループで各バンドルIDに該当するアプリアイコンをダイアログ表示します。

bbe.png

twa.png

find.png

AppleScript名:指定バンドルIDのアプリケーションのアイコンへのパスを求める
– Created 2017-02-14 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4460

set iconList to {“com.barebones.bbedit”, “com.barebones.textwrangler”, “com.apple.Finder”}
repeat with i in iconList
  set aPath to retIconPath(i) of me
  
if aPath is not equal to false then
    display dialog “ICNS test” with title i with icon ((POSIX file aPath) as alias) buttons {“OK”} default button 1
  end if
end repeat

–指定バンドルIDのアプリケーションのアイコンのフルパスを取得する
on retIconPath(anID)
  set aRes to chkAppIsInstalled(anID) of me
  
if aRes = false then return false
  
set appPathPOSIX to (current application’s NSWorkspace’s sharedWorkspace()’s absolutePathForAppBundleWithIdentifier:anID) as string
  
set anIconPath to getIconFilePath(appPathPOSIX) of me
  
return anIconPath
end retIconPath

–指定バンドルIDのアプリケーションが存在しているかチェック
on chkAppIsInstalled(aBundleID as string)
  set aRes to current application’s NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
set bRes to (aRes is not equal to missing value)
  
return bRes
end chkAppIsInstalled

–指定ファイルのアイコンのファイルのパスを取得する
on getIconFilePath(aPOSIXPath)
  set anURL to current application’s |NSURL|’s fileURLWithPath:aPOSIXPath
  
set myBundle to current application’s NSBundle’s bundleWithURL:anURL
  
set aBundleID to myBundle’s bundleIdentifier() as text
  
set anIconFileName to (myBundle’s objectForInfoDictionaryKey:“CFBundleIconFile”) as string
  
  
if anIconFileName does not end with “.icns” then
    set anIconFileName to anIconFileName & “.icns”
  end if
  
  
set ifonFileFullPath to aPOSIXPath & “/Contents/Resources/” & anIconFileName
  
return ifonFileFullPath
end getIconFilePath

★Click Here to Open This Script 

2017/02/14 Bare Bones SoftwareがTextWranglerを廃止してBBEditに一本化

Mac系の老舗ソフトウェア開発会社Bare Bones Softwareのフリー版テキストエディタ「TextWrangler」が廃止になり、BBEditに一本化されることが2017年初頭に発表になっていました。

bbsoftware_resized.png

たまたま用事があって、Bare Bones Softwareに連絡をとったら「TextWranglerが廃止になった」ことを知りました。すぐにWebサイト上からダウンロードできなくなるわけではないとは思いますが、時間の問題でしょう。

ちなみに、BBEditは同社のWebサイトからお試し版がダウンロードできて、30日間無料で試用が可能です。30日をすぎたあとは、TextWrangler相当の永遠お試し版として使い続けられる様子です。

bbeditandtw_resized.png

たしかに、TextWranglerはBBEditの「お試し版」的な位置付けで開発され、広く愛用されたソフトウェアではありました。しかし昨今では、逆にTextWranglerの知名度が上がりすぎてBBEditへの移行を促す販促製品としてはふさわしくない存在になっていたことも事実であります。

自分の常用しているテキストエディタは、mi、TextWranglerの2本。どちらかといえば、TextWranglerの方が多いかなといったところ。

BBEditについては、「重そう」とか「HTMLコンテンツ作成系の不要な機能がいっぱいついていてメニューがごたごたしている」といった印象を持っていたのですが、(久しぶりに)実際に動かしてみると、それほど違いがあるわけでもないようです(メニューが少し多いかな、ぐらい?)。

AppleScript用語辞書について、TextWranglerをそれほど重箱の隅までつつきまくったわけではなかったのですが、比較してみたら(ささいな違いをのぞくと)31か所違っていました。BBEditの方が多機能です。

asdictdiff_resized.png

ただ、TextWrangler用のAppleScriptはほぼそのままBBEdit用に転用できるので、何も考えないレベルで使いまわしが可能でしょう。

2017/02/12 GPUImageで輪郭抽出フィルタを実行

オープンソースのフレームワーク「GPUImage」(By Brad Larson)を呼び出して、指定画像に輪郭抽出フィルタを実行して、デスクトップにフィルタ名でPNGに保存するAppleScriptです。

テストにあたっては、GPUImageフレームワークをXcodeでビルドして、~/Library/Frameworksフォルダに入れておいてください。

実際にはもっと凝った処理に使っていますが、その過程でフィルタ名を文字列で間接指定して実行するルーチンが出来てきて、なかなか使い勝手もよくなってきています。

気になる処理速度ですが、MacBook Pro Mid 2012 Core i7 2.6GHzの環境で、

 3,264 × 2,448 pixel、1.2MB → 0.7sec
 3,024 × 4,032 pixel、2.8MB → 1.2sec

ぐらいでした(10回計測時の平均。2回目以降はキャッシュが効いている可能性もあります)。処理中にはこの4Core/8Threadの環境でCPUのロードアベレージが20%ぐらいでまだ余裕があり、AppleScriptで並列処理するともうちょっと速度を稼げるかもしれません。

sample1.png

sample2.png

AppleScript名:GPUImageで輪郭抽出フィルタを実行
– Created 2017-02-12 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “GPUImage” –https://github.com/BradLarson/GPUImage
use framework “AppKit”
–http://piyocast.com/as/archives/4451

set aFile to POSIX path of (choose file of type {“public.image”})
set anImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile
set imgRes to filterWithNSImage(anImage, “GPUImageSobelEdgeDetectionFilter”) of me
set newPath to retUUIDfilePath(aFile, “png”) of me
set sRes to saveNSImageAtPathAsPNG(imgRes, newPath) of me

on retUUIDfilePath(aPath, aEXT)
  set aUUIDstr to (current application’s NSUUID’s UUID()’s UUIDString()) as string
  
set aPath to ((current application’s NSString’s stringWithString:aPath)’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:aEXT
  
return aPath
end retUUIDfilePath

on filterWithNSImage(aNSImage, filterName as string)
  set aClass to current application’s NSClassFromString(filterName)
  
set aImageFilter to aClass’s alloc()’s init()
  
set aProcImg to (aImageFilter’s imageByFilteringImage:aNSImage)
  
return aProcImg
end filterWithNSImage

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes –true/false
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2017/02/09 Application Supportフォルダを求める

Application Supportフォルダを求めるAppleScriptです。

ただ、通常環境(Script Editorとか)を前提としたものではなく(動きますけど)、Xcode上のCocoa-AppleScript Applet内でCodeSignしてApp Sandboxを有効にした環境におけるApplication Supportフォルダを求めるためのものです。

一応、Script Editorや他のAppleScript開発環境でも動きますが、Sandbox環境では返ってくるパスが全然違います。そうした一種の極限環境でも値を取得していろいろやるための実験であります。

AppleScript名:Application Supportフォルダを求める
– Created 2017-02-09 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4450

set fileManager to current application’s NSFileManager’s alloc()’s init()
set bundleID to current application’s NSBundle’s mainBundle()’s bundleIdentifier()
set urlPaths to fileManager’s URLsForDirectory:(current application’s NSApplicationSupportDirectory) inDomains:(current application’s NSUserDomainMask)
set appDirectory to (((urlPaths’s objectAtIndex:0)’s URLByAppendingPathComponent:bundleID isDirectory:true)’s |path|()) as string

–> “/Users/me/Library/Application Support/au.com.myriad-com.ASObjC-Explorer-4″–ASObjC Explorer 4の場合
–> “/Users/me/Library/Application Support/com.apple.ScriptEditor2″–Apple純正のScript Editorの場合

★Click Here to Open This Script 

2017/02/09 choose file,choose folderの始点ディレクトリ指定にPOSIX pathが使える

以前からチラホラ話は聞いていたのですが、choose fileやchoose folderの始点ディレクトリ(コマンド実行時にファイル/フォルダ選択ダイアログに表示されるフォルダ)を指定する「default location」オプション。

この「default location」オプションにPOSIX pathが指定できますよ、というお話です。

pathobjects.png

AppleScript/Cocoa関連の主要なパスオブジェクトはだいたいこの図のようになっていて、最近はCocoaに渡すためにPOSIX pathの登場頻度が高くなっています。ただ、よく出てくるとはいっても、「何かに変換するための一時的な形式」であることが多いようです(個人的に)。

このchoose file/choose folderの始点ディレクトリにPOSIX pathが使えるようになっており、おそらくXcode上で作成するCocoa-AppleScript Applet内で事実上aliasが使えないので、その対策として付加された機能と推測しています(根拠はありません)。ちなみに、同環境でaliasが使いたければ、.scpt形式あるいは.scptd形式のScriptとしてXcodeのプロジェクトに入れて、ASOC側からload scriptしつつ呼び出すような処理になると思います(ライブラリにするやりかたもあるかも)。

AppleScript名:choose fileの始点にPOSIX path
choose file default location “/Users/me/Documents”

★Click Here to Open This Script 

AppleScript名:choose folderの始点にPOSIX path
choose folder default location “/Users/me/Desktop”

★Click Here to Open This Script 

始点ディレクトリはフルパスで指定する必要があり、「~」を併用した記法は使えませんでした。

ちなみに、存在しないPOSIX pathを指定すると、

choose_posix_alias.png

のようなエラーが表示されます。コマンド内部ではaliasに変換しているようです。

この仕様がいつごろから実装されているのかはわかりませんが、おそらくmacOS 10.6以降ではないかと推測されます(AppleScript StudioからAppleScriptObjCへの移行期あたり)。

2017/02/08 指定文字列一括消去

複数の文字列を一括で消去するAppleScriptです。

unicodechecker_65532.png

ASOCでどうしても置換できないゴミ文字列(string id 65532)に遭遇し、Cocoaベースの置換ルーチンからPure AppleScriptの置換ルーチンに置き換えることになりました。

その折、複数回の置換処理を行なっている部分が気になったので、複数の文字を一括で置換するようにしてみたのが本ルーチンです。消去対象の文字列同士のコンフリクトなどは一切チェックしていません。

当初、一括置換ルーチンも掲載していたのですが、動作内容に納得がいかなかったので削除しました。

AppleScript名:指定文字列一括消去
– Created 2017-02-08 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4445

set aText to “Gundam, Guncannon, Guntank, Zak, Gufu, Gogg, Gergog, Zeong, Neo-Zeong”
set deleteTargList to {“Gun”, “Zeon”}

set aRes to removeChars(aText, deleteTargList) of me
–> “dam, cannon, tank, Zak, Gufu, Gogg, Gergog, g, Neo-g”

–指定文字列一括消去
on removeChars(origText as string, targStr as list)
  set aLen to length of targStr
  
set repStr to {}
  
repeat with i from 1 to aLen
    set the end of repStr to “”
  end repeat
  
  
set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end removeChars

★Click Here to Open This Script 

2017/02/07 Apple Mailing Listsが復旧

US Appleがホスティングしている開発系(&営業系、ユーザー系、政府官公庁系、学校系)メーリングリスト(以下、ML)が、どうやら本日復旧したようです。

週明け(昨日)に復旧していなかったので「まだ復旧しないんだけど?(怒)」とDeveloper Connectionsに電話。その翌日に復旧と相成った次第です(疲れた)。MLのサーバーは会社規模のサーバー管理部署とも違っていそうだし、Developer Supportも当事者ではなさそうだし、いまひとつどこの誰が担当しているのか分かりにくい存在です。

トラブル原因や経過についてはとくに説明もなく、あっけなく復旧。ただ、自分がしつこく口うるさく小突き回したから復旧した、とも思えません。

# 情報収集してみたところ(というよりも、当人から直接教えてもらったんですが)、MLの某メンバーが直接US Appleの社員に電話して、その社員からSysopに復旧依頼を出してもらったもよう

そもそも、MLに問題があったときの連絡先メールアドレスが、その当のMLサーバー上のアドレスに設定してある、という運用はマヌケではないのか? MLサーバーの管理担当者の連絡先は、他のメールサーバー上に設定しておくべきではないのか? といったことは考えないんでしょうか?

postmaster_is_burning.png

(AppleScript系以外の)他のML参加者は、「MLが停まっていた」という事態はあまり認識していなかったようで、「なんで流れてこなかったんだろうねぇ」ぐらいの呑気な状況(ーー;;

全ML参加制覇(ほとんど読んで分析するだけ)&全MLからのAppleScriptによる情報抽出ロボット運用を行なっている唯一の存在であることが、はからずしも明らかになってしまいました(^ー^;

# 過去アーカイブがまだ復旧していないもよう(汗)

2017/02/05 GPUImageで画像にすべてのフィルタを実行してデスクトップにPNG形式で保存

オープンソースのフレームワーク「GPUImage」(By Brad Larson)を呼び出して、指定画像にGPUImageのすべてのフィルタを実行して、デスクトップにフィルタ名でPNGに保存するAppleScriptです。

テストにあたっては、GPUImageフレームワークをXcodeでビルドして、~/Library/Frameworksフォルダに入れておいてください。

ドキュメントによれば125種類ものフィルタを内蔵しているというGPUImageですが、どれがどのような処理を行ってくれるのかは、実際に呼び出してみないとわかりません。また、単に呼び出して画像にフィルタを実行しただけでは記述内容が不足しているというものもありそうです(パラメータを指定しないと意味がないケース、フィルタが仕様の都合で複数の画像を要求するものも)。

そこで、ヘッダーファイルからすべてのフィルタ名称を取り出して、ループで順次実行させてみました。デスクトップにフィルター名称でPNG画像を書き出します。

ヘッダーファイルに記載されていたフィルターとおぼしきものが145、うち実行可能だったものが100、エラーが45となりました。また、エラーは出なかったものの何らかのパラメータを指定しないとおそらく意味がない(オリジナル画像と変わらない)ものもありました。

samples_out.png

フィルタをallocしてinitした段階でエラーが出るものもあったり、エラーをキャッチできるまでに時間がかかるものもありましたが、with timedoutでタイムアウト時間を指定してもエラーとして検出することはできませんでした。

フレームワークを呼び出すのはAppleEventの枠組みの中でやっているわけではないはずなので、タイムアウトを仕掛けて適用されなくても仕方ありません。

AppleScript名:GPUImageで画像にすべてのフィルタを実行してデスクトップにPNG形式で保存
– Created 2017-02-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “GPUImage”
–https://github.com/BradLarson/GPUImage
–http://piyocast.com/as/archives/4441

–Read JPEG file
set aFile to POSIX path of (choose file of type {“public.image”})
set anImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile

–Select Filter
set erroredFilter to {}
set aList to {“GPUImageFilter”, “GPUImageTwoPassFilter”, “GPUImage3×3TextureSamplingFilter”, “GPUImageContrastFilter”, “GPUImageSaturationFilter”, “GPUImageBrightnessFilter”, “GPUImageLevelsFilter”, “GPUImageExposureFilter”, “GPUImageRGBFilter”, “GPUImageHueFilter”, “GPUImageWhiteBalanceFilter”, “GPUImageMonochromeFilter”, “GPUImagePixellateFilter”, “GPUImageSobelEdgeDetectionFilter”, “GPUImageSketchFilter”, “GPUImageToonFilter”, “GPUImageGrayscaleFilter”, “GPUImageKuwaharaFilter”, “GPUImageFalseColorFilter”, “GPUImageSharpenFilter”, “GPUImageUnsharpMaskFilter”, “GPUImageTwoInputFilter”, “GPUImageGaussianBlurFilter”, “GPUImageTwoPassTextureSamplingFilter”, “GPUImageFilterGroup”, “GPUImageTransformFilter”, “GPUImageCropFilter”, “GPUImageGaussianBlurPositionFilter”, “GPUImageGaussianSelectiveBlurFilter”, “GPUImageBilateralFilter”, “GPUImageBoxBlurFilter”, “GPUImageSingleComponentGaussianBlurFilter”, “GPUImageMedianFilter”, “GPUImageMotionBlurFilter”, “GPUImageZoomBlurFilter”, “GPUImageAddBlendFilter”, “GPUImageColorBurnBlendFilter”, “GPUImageDarkenBlendFilter”, “GPUImageDivideBlendFilter”, “GPUImageLightenBlendFilter”, “GPUImageMultiplyBlendFilter”, “GPUImageOverlayBlendFilter”, “GPUImageColorDodgeBlendFilter”, “GPUImageLinearBurnBlendFilter”, “GPUImageScreenBlendFilter”, “GPUImageColorBlendFilter”, “GPUImageExclusionBlendFilter”, “GPUImageHueBlendFilter”, “GPUImageLuminosityBlendFilter”, “GPUImageNormalBlendFilter”, “GPUImagePoissonBlendFilter”, “GPUImageSaturationBlendFilter”, “GPUImageSoftLightBlendFilter”, “GPUImageHardLightBlendFilter”, “GPUImageSubtractBlendFilter”, “GPUImageTwoInputCrossTextureSamplingFilter”, “GPUImageDifferenceBlendFilter”, “GPUImageDissolveBlendFilter”, “GPUImageChromaKeyBlendFilter”, “GPUImageMaskFilter”, “GPUImageOpacityFilter”, “GPUImageAlphaBlendFilter”, “GPUImageColorMatrixFilter”, “GPUImageSepiaFilter”, “GPUImageGammaFilter”, “GPUImageHazeFilter”, “GPUImageToneCurveFilter”, “GPUImageHighlightShadowFilter”, “GPUImageLookupFilter”, “GPUImageAmatorkaFilter”, “GPUImageMissEtikateFilter”, “GPUImageSoftEleganceFilter”, “GPUImage3×3ConvolutionFilter”, “GPUImageEmbossFilter”, “GPUImageLaplacianFilter”, “GPUImageLanczosResamplingFilter”, “GPUImageThreeInputFilter”, “GPUImageFourInputFilter”, “GPUImageColorInvertFilter”, “GPUImageHistogramFilter”, “GPUImageHistogramGenerator”, “GPUImageAverageColor”, “GPUImageLuminosity”, “GPUImageSolidColorGenerator”, “GPUImageAdaptiveThresholdFilter”, “GPUImageAverageLuminanceThresholdFilter”, “GPUImageLuminanceThresholdFilter”, “GPUImageSolarizeFilter”, “GPUImageHalftoneFilter”, “GPUImagePixellatePositionFilter”, “GPUImagePolarPixellateFilter”, “GPUImagePolkaDotFilter”, “GPUImageCrosshatchFilter”, “GPUImageXYDerivativeFilter”, “GPUImageDirectionalNonMaximumSuppressionFilter”, “GPUImageDirectionalSobelEdgeDetectionFilter”, “GPUImageCannyEdgeDetectionFilter”, “GPUImagePrewittEdgeDetectionFilter”, “GPUImageThresholdEdgeDetectionFilter”, “GPUImageHarrisCornerDetectionFilter”, “GPUImageNobleCornerDetectionFilter”, “GPUImageShiTomasiFeatureDetectionFilter”, “GPUImageThresholdedNonMaximumSuppressionFilter”, “GPUImageColorPackingFilter”, “GPUImageHoughTransformLineDetector”, “GPUImageParallelCoordinateLineTransformFilter”, “GPUImageCrosshairGenerator”, “GPUImageLineGenerator”, “GPUImageBuffer”, “GPUImageLowPassFilter”, “GPUImageHighPassFilter”, “GPUImageMotionDetector”, “GPUImageThresholdSketchFilter”, “GPUImageSmoothToonFilter”, “GPUImageTiltShiftFilter”, “GPUImageCGAColorspaceFilter”, “GPUImagePosterizeFilter”, “GPUImageKuwaharaRadius3Filter”, “GPUImageChromaKeyFilter”, “GPUImageVignetteFilter”, “GPUImageBulgeDistortionFilter”, “GPUImagePinchDistortionFilter”, “GPUImageStretchDistortionFilter”, “GPUImageClosingFilter”, “GPUImageRGBClosingFilter”, “GPUImageDilationFilter”, “GPUImageRGBDilationFilter”, “GPUImageErosionFilter”, “GPUImageRGBErosionFilter”, “GPUImageOpeningFilter”, “GPUImageRGBOpeningFilter”, “GPUImageSphereRefractionFilter”, “GPUImageGlassSphereFilter”, “GPUImageSwirlFilter”, “GPUImageJFAVoronoiFilter”, “GPUImageVoronoiConsumerFilter”, “GPUImageLocalBinaryPatternFilter”, “GPUImageColorLocalBinaryPatternFilter”, “GPUImageMosaicFilter”, “GPUImagePerlinNoiseFilter”, “GPUImageWeakPixelInclusionFilter”, “GPUImageNonMaximumSuppressionFilter”, “GPUImageSourceOverBlendFilter”, “GPUImageColourFASTFeatureDetector”, “GPUImageColourFASTSamplingOperation”}

repeat with i in aList
  set j to contents of i
  
set aClass to current application’s NSClassFromString(j)
  
  
–Filter Image
  
set errorFlag to true
  
try
    with timeout of 5 seconds
      set stillImageFilter to aClass’s alloc()’s init()
      
set aProcImg to (stillImageFilter’s imageByFilteringImage:anImage)
    end timeout
  on error erM
    set the end of erroredFilter to {j, erM, 1}
    
set errorFlag to false
  end try
  
  
if errorFlag = true then
    –Make New File Name
    
set aPath to (((current application’s NSString’s stringWithString:aFile)’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:j)’s stringByAppendingPathExtension:“png”)
    
try
      set sRes to saveNSImageAtPathAsPNG(aProcImg, aPath as string) of me
    on error erM
      set the end of erroredFilter to {j, erM, 2}
    end try
  end if
end repeat

return erroredFilter
–> {{”GPUImageTwoInputFilter”, “missing valueは“representationUsingType_properties_”メッセージを認識できません。”, 2}, …..

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes –true/false
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2017/02/05 GPUImageで画像にGPUImageMonochromeFilterを実行してデスクトップにPNG形式で保存

オープンソースのフレームワーク「GPUImage」(By Brad Larson)を呼び出して、指定画像にフィルタ(モノクローム画像化)を実行して、デスクトップにPNGで保存するAppleScriptです。

テストにあたっては、GPUImageフレームワークをXcodeでビルドして、~/Library/Frameworksフォルダに入れておいてください。

GPUImageはもともとiOSデバイス用に作られたようですが、Macもサポートしており、さまざまな(125種類もの)画像フィルタが用意されており、手軽に利用できます。CoreImageをAppleScriptから呼び出してフィルタを実行してみたこともありますが、CoreImageよりも手軽に感じられました。

フィルタ実行部分は2行だけで、ファイルを選択したり画像を保存する部分がほとんどなので、やることはほとんどありません。ある意味、Photoshopを呼び出すよりも手軽です。

img_0007_resized.png
▲実行前のオリジナル画像(Haneda Airport, Tokyo, Japan)

a5dfc34c-d1e4-47e6-8379-f46412dbc30d_resized.png
▲GPUImageMonochromeFilterを実行した画像

AppleScript名:GPUImageで画像にGPUImageMonochromeFilterを実行してデスクトップにPNG形式で保存
– Created 2017-02-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “GPUImage”
–https://github.com/BradLarson/GPUImage
–http://piyocast.com/as/archives/4438

–Read JPEG file
set aFile to POSIX path of (choose file of type {“public.image”})
set anImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile

–Filter Image
set stillImageFilter to current application’s GPUImageMonochromeFilter’s alloc()’s init()
set aProcImg to stillImageFilter’s imageByFilteringImage:anImage

–Make New File Name
set aUUIDstr to (current application’s NSUUID’s UUID()’s UUIDString()) as string
set aPath to ((current application’s NSString’s stringWithString:aFile)’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:“png”
set sRes to saveNSImageAtPathAsPNG(aProcImg, aPath as string) of me

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes –true/false
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2017/02/03 倍率を指定してNSImageをリサイズする

与えられたNSImageをリサイズするAppleScriptです。

実行サンプルとして、実行中のコンピュータの画像(NSImageNameComputer)をNSImageで取得、このNSImageを16倍の大きさにリサイズし、指定場所&名称でPNG形式で保存します。

AppleScript名:NSImageの倍率を変更してNSImageをリサイズする
– Created 2017-02-03 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://stackoverflow.com/questions/11949250/how-to-resize-nsimage
–http://piyocast.com/as/archives/4437

set aPath to POSIX path of (choose file name with prompt “Enter PNG file name”)

–Get Computer Icon
set anImage to current application’s NSImage’s imageNamed:(current application’s NSImageNameComputer)

–Resize it to x16
set resizedImg to my resizedImage:anImage toScale:16
set aRes to saveNSImageAtPathAsPNG(resizedImg, aPath) of me

on resizedImage:aSourceImg toScale:imgScale
  if (aSourceImg’s isValid()) as boolean = false then error “Invalid NSImage”
  
  
set aSize to aSourceImg’s |size|()
  
–>  {width:32.0, height:32.0}
  
  
set aWidth to (aSize’s width) * imgScale
  
set aHeight to (aSize’s height) * imgScale
  
  
set aRep to current application’s NSBitmapImageRep’s alloc()’s initWithBitmapDataPlanes:(missing value) pixelsWide:aWidth pixelsHigh:aHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:true isPlanar:false colorSpaceName:(current application’s NSCalibratedRGBColorSpace) bytesPerRow:0 bitsPerPixel:0
  
  
set newSize to {width:aWidth, height:aHeight}
  
aRep’s setSize:newSize
  
  
current application’s NSGraphicsContext’s saveGraphicsState()
  
current application’s NSGraphicsContext’s setCurrentContext:(current application’s NSGraphicsContext’s graphicsContextWithBitmapImageRep:aRep)
  
  
aSourceImg’s drawInRect:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) fromRect:(current application’s NSZeroRect) operation:(current application’s NSCompositeCopy) fraction:(1.0)
  
  
current application’s NSGraphicsContext’s restoreGraphicsState()
  
  
set newImg to current application’s NSImage’s alloc()’s initWithSize:newSize
  
newImg’s addRepresentation:aRep
  
  
return newImg
end resizedImage:toScale:

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  –画像のRaw画像を作成
  
set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
  
–書き出しファイルパス情報を作成
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
–書き出し
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes –成功ならtrue、失敗ならfalseが返る
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2017/02/03 2つのパスの相対パスを求める v3

2つのPOSIX pathの相対位置を求めるAppleScriptです。aFileの位置から見た、bFileの相対パス表記を求めます。

MacDown(Mac用のMarkdownエディタ)の作者のuranusjrに「ねー相対パス表記サポートしてよー」とお願いしたところ、単に自分の相対パス表記の書き方が間違っていたことが判明。正しい表記で書いたら現行のMacDown v0.6.4でも問題なく画像への相対パスによるリンク指定ができました。

uranusjrもShane Stanleyも誰も悪くありません。自分が間違っていただけです。

というわけで、相対パス計算ルーチンを修正しておきました。

本ルーチンは、実際にMarkdownで記述した書籍本文中の画像リンク(Dropbox上に設定していた)から、画像をローカルの所定のフォルダにすべてダウンロードし、リンク先をローカル(相対パス指定)に書き換えるAppleScriptで使用しました。

AppleScript名:2つのパスの相対パスを求める v3
– Created 2017-01-28 by Takaaki Naganoya
– Modified 2017-01-30 by Shane Stanley
– Modified 2017-02-03 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4436

set aFile to “/Users/me/Documents/Book1/0900 Command Reference/1000 Command References/1010 tell/1010 tell.md”
set bFile to “/Users/me/Documents/Book1/9999_images/002-640×427.png”

set relativePath to calcRelativePath(aFile, bFile) of me
–>  ”../../../9999_images/002-640×427.png”

on calcRelativePath(aPOSIXfile, bPOSIXfile)
  set aStr to current application’s NSString’s stringWithString:aPOSIXfile
  
set bStr to current application’s NSString’s stringWithString:bPOSIXfile
  
  
set aList to aStr’s pathComponents() as list
  
set bList to bStr’s pathComponents() as list
  
  
set aLen to length of aList
  
set bLen to length of bList
  
  
if aLen bLen then
    copy aLen to aMax
  else
    copy bLen to aMax
  end if
  
  
repeat with i from 1 to aMax
    set aTmp to contents of item i of aList
    
set bTmp to contents of item i of bList
    
    
if aTmp is not equal to bTmp then
      exit repeat
    end if
  end repeat
  
  
set bbList to items i thru -1 of bList
  
set aaItem to (length of aList) - i
  
  
set tmpStr to {}
  
repeat with ii from 1 to aaItem
    set the end of tmpStr to “..”
  end repeat
  
  
set allRes to current application’s NSString’s pathWithComponents:(tmpStr & bbList)
  
return allRes as text
end calcRelativePath

★Click Here to Open This Script 

2017/02/03 Xcode上のAppleScript編集で困ったトラブル

macOS, Sierra上でXcode 8.2を使ってAppleScriptのプログラムを組んでいたら、Xcodeがクラッシュ。

Xcodeのクラッシュ自体は珍しくもなんともありません。落とす気になれば、いつでも落とせる操作がいくつも存在するXcode。Xcodeが落ちるだけでなく、クラッシュの道連れになって、作っていたXcode Project(Cocoa-AppleScript applet)がおかしなことに。

Xcode内のInterfece BuilderでDelegateを選んでControlクリック。Outletなどの選択(GUI部品とのひもづけ)が行える黒い半透明のパレットが表示されるわけですが、

xcode1_resized.png

のきなみ⚠マークが表示され、あろうことか未接続のOutletや、

iboutlets.png

既存のイベントハンドラまで見えなくなってしまいました。

eventhandlers.png

さらに、おそるべきことにXcode上でAppleScriptを編集してIBOutletを追加したり、イベントハンドラを追加したり、逆にこれらをすべて削除してみてもInterface Builder上では反映されません。ほとんどホラー映画の世界です。削除したハンドラやOutletの情報は一体どこに残っているというのでしょう?

# こういう未経験のトラブルに直面したときに、Mailing Listに話を投げておくと、地球の裏側の時間帯のユーザーで知っている人がいれば(自分が寝ている間に)答えてもらえたりで、とってもMLは便利なんですが、いま動いていないし、、、

あわてて、Xcodeのバージョンを上げたり(8.2→8.3.1Beta)、下げたり(→7.3.1)、何かのOSAXやOSA言語コンポーネントが悪影響を及ぼしているのではないかと疑って調査してみたものの・・・どれも解決策になりません。

並行してedama2さんに相談。問題のXcode Projectをメールで送ってあーでもないこーでもない、と情報交換。

試行錯誤のすえに、問題らしきものが見えてきました。

xcode3.png

XcodeでAppleScriptのファイルを選択し、コンテクストメニューから「Open With External Editor」を選択し、自分の環境では「.applescript」にひもづけてあるASObjC Explorer 4でオープン。ASObjC Explorer 4で編集して保存してみたところ、XcodeにもどりInterface Builder上で、、、、

xcode2_resized.png

正常にOutletやEvent Handler(Received Actions)が表示されるようになりました(!!!!)。

改行コードや文字コードなど? Xcode上で編集中に一部の情報がおかしくなった部分が、外部エディタによる編集で修正されるのか、はたまた外部エディタで保存を行うことでXcode内でScriptの読み直しを行うためでしょうか。

何が原因なのか、正しく追求できているわけではないので、何によって解決されているのかを正しく表現することは困難です。ただし、このような状況に陥ったときに一度お試しいただきたい対処方法であります。

追伸:edama2さん、本当にいろいろお試しいただきありがとうございます。

2017/02/01 続・US Appleの各種MLが先週末からダウン

US Appleが主催している開発系(&営業系、ユーザー系、政府官公庁系、学校系)メーリングリスト(以下、ML)が先週末(2017/1/28)からダウンしています。

ml_lastupdate.png

postmasterにメールしたものの、メールが受信されている様子がなく、仕方なくCEO直メールを行なったというのが前回までのあらすじです。

で、現地が深夜にもかかわらずメールの返事が(CEO直轄チームの誰か)から来て、「いい方向」に(勝手に)受け止めてしまったのですが、文面を読み直すと、

Mailing Listについて知らないApple社内の人間が、

『まーた、メールの調子がよくないとかしょーもない内容のメールを送ってきたよ。どうせメーラーの設定がわかんないとかそういうレベルだろ。いるいる、こういうの。こいつ、Apple Careのサポートにまわしといて〜』

的な扱い方をした(優雅な文面で)という状況。全力でスルーされた模様です。

Apple社内でMLサーバーを管理している(放置している)部署がいまひとつわからないので、Developper Support経由で、

「こういう問題が起きて困っている。あなたの部署の案件ではないかもしれないが、社外からでは適切なエスカレーションの窓口がどこなのかわからない。調べてもらえないか?」

と、電話してお願いしました。極東の島国の1零細デベロッパー(自分)が騒いでいるぐらいなので、他の国の連中も騒いでいると思うのですが、不思議とそれが表面化しません。

# 案外、すべてのMLに入っていてAppleScriptのプログラムで高度なメールの仕分けをしているような変な人種しか、この異常事態に気づかないのかもしれません。あ、自分のことか、、、

すぐに折り返しでUS Appleのデベロッパーサポート(日本語を話せる人)から電話が来て、

 ・この件については社内で認識していなかった(まあそうでしょう)
 ・もういちど、postmaster宛にメールを投げて様子を見てくれ
 ・すぐには対応できない可能性が高い。申し訳ない

という趣旨の説明をされました(本当に申し訳ない、という感じで)。自分も「まあ、そうなりますよねー」という返事しか出てきません。Tim Cookにメールした、といったら大ウケしていました(汗)。

Apple社内は超縦割り構造で、他部署が管轄している情報にアクセスできないし、部署を飛び越えて何かのアクションを行いにくい状態になっていることは知っています。

もし、今回のMLサーバーの問題が、外部からのクラッキングへの結果として発生した可能性があれば、問題追及のためにさらなる調査が必要でしょう(数年に一度、勝手に落ちてるんでその可能性は低そうなんですけれど)。

ただ、この社内構造は「Steve Jobsという部署間の垣根や階層を取り払って活動できるスーパーバイザー」がいる時のものであり、彼亡きあともこの構造を維持することに意味があるのかどうか、自分自身は懐疑的な立場です。