画像ファイルを読み込んで、クリップボードに設定するAppleScriptです。
標準のAppleScriptの命令だけで割と簡潔に書けることに驚きます。
AppleScript名:画像ファイルを読み込んでクリップボードに設定する |
set aFile to choose file of type {"public.image"} set anImage to read aFile as TIFF picture set the clipboard to anImage |
画像ファイルを読み込んで、クリップボードに設定するAppleScriptです。
標準のAppleScriptの命令だけで割と簡潔に書けることに驚きます。
AppleScript名:画像ファイルを読み込んでクリップボードに設定する |
set aFile to choose file of type {"public.image"} set anImage to read aFile as TIFF picture set the clipboard to anImage |
SMCWrapperをCocoa Framework化してAppleScriptから呼びやすいように書き換えた「SMCKit.framework」を呼び出して、CPUの温度や放熱ファンの回転数を取得するAppleScriptです。
AppleScriptからCocoaを呼び出して高速処理を行っていたら、CPUの温度が上がりすぎて焦ったことがありました。これは、対策をしておいたほうがよいでしょう。
Macの放熱ファン制御&温度管理ソフトウェアで、最近評判がいいのはTunabelly Softwareの「TG Pro」。
ただし、これはAppleScriptから制御できるタイプのソフトウェアではなかったので、別の手段を探してみました(MacBook Proの放熱強化のためには利用しています)。
SMC(System Management Controller)からCPUの温度を取得するオープンソースのプログラム「SMCWrapper」を見つけたのですが、すでにメンテナンスされておらず、しかもAppleScriptから呼び出しやすい構造になっていなかったため、Cocoa Framework化しただけで手元に放置してありました。
— Created 2017-07-28 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "SMCKit"
set numberOfFuns to current application’s NSString’s new()
current application’s SMCWrapper’s readKey:"FNum" asString:(numberOfFuns)
★Click Here to Open This Script
↑こんな風に呼んでは、動かないことを確認していました(↑は本当に動きません)。
CPUの温度は取得できたほうがよさそうだったので、SMCWrapperにAppleScriptから呼び出しやすいようにメソッドを追加して呼び出してみました。
–> Download SMCKit.framework(To ~/Library/Frameworks/)
readKeyAsString: というメソッドだけ新設して使っていますが、けっこう便利です。
AppleScript名:SMCkitのじっけん v2.scptd |
— Created 2018-08-21 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "SMCKit" –https://github.com/FergusInLondon/SMCWrapper –I turned the SMCWrapper into Cocoa Framework and change it to call from AppleScript set smc to current application’s SMCWrapper’s sharedWrapper() –FNum => Number of Fans set a0Res to (smc’s readKeyAsString:"FNum") as list of string or string –as anything –> 2 — TC0P => CPU Temperature set a1Res to smc’s readKeyAsString:"TC0P" –> 57.125 — F0Ac => Fan0 Actual RPM set a2Res to smc’s readKeyAsString:"F0Ac" –> 5891.00 — F0Mn => Min RPM set a3Res to smc’s readKeyAsString:"F0Mn" –> 5888.00 — F0Mx => Max RPM set a4Res to smc’s readKeyAsString:"F0Mx" –> 5940.00 if a0Res ≥ 2 then — F1Ac => Fan1 Actual RPM set a5Res to smc’s readKeyAsString:"F1Ac" –> 5439.00 — F1Mn => Min RPM set a6Res to smc’s readKeyAsString:"F1Mn" –> 5440.00 — F1Mx => Max RPM set a7Res to smc’s readKeyAsString:"F1Mx" –> 5940.00 end if |
Terminal.appでコマンドを実行するAppleScriptです。
AppleScriptからshell commandを実行するならdo shell scriptコマンドを経由することが多いので、Terminal.appをコントロールしてshell commandを実行しているのは「よくわかっていない人」だけです。これをやるとMac App Storeで一発でリジェクトされる処理内容でもあります。
ただ、そうはいってもごくまれにTerminal.app上でshell commandを実行する必要に迫られて(10年に1度ぐらいの割合で)、仕方なくTerminal.appをコントロールすることになりますが、Windowの有無を確認してその場合に応じて書き方を若干変える必要があるのが面倒。なので、このようにサブルーチンにして使い回すのが一般的なやりかたです。
■本ルーチン使用例
・2つのPDFのテキストの指定ページの差分をVimdiffで表示する v2
この例のほかはssh経由でhostにログインしてコマンド実行したときぐらいで、この2例が「本当にTerminal.appを操作しないと処理できない例」で、ほかは99.9%do shell scriptコマンドで済みました。
ごくたまに、Terminalからパラメータを入力することだけを前提に作られている、出来のよろしくないCLIツールも存在します。
普通のCLIツールのように「フルパスを指定しても問題ない」のではなく、「フルパスでパス指定すると不具合を引き起こす」ことには腰を抜かしました。そういうものもあるので、実際にdo shell scriptコマンド経由で自分の望む機能を呼び出せるかは、試してみないと(試してみるまで)わかりません。機械学習系(画像の物体識別)でそういうものに遭遇しました。落とし穴としかいいようがありません。
AppleScript名:Terminal.appでコマンドを実行する |
use AppleScript version "2.4" use scripting additions use framework "Foundation" set aCom to "ps" doComInTerminalWindow(aCom) of me on doComInTerminalWindow(aCMD as string) using terms from application "Terminal" tell application id "com.apple.Terminal" activate set wCount to count (every window whose visible is true) if wCount = 0 then –ウィンドウが1枚も表示されていない場合 do script aCMD else –すでにウィンドウが表示されている場合 do script aCMD in front window end if end tell end using terms from end doComInTerminalWindow |
Skimで現在表示中のPDFの現在のページ番号を、Numbers書類で選択中のセルに入れて、Numbersの選択中のセルを1つ下に移動させるAppleScriptです。
作業をちょっとだけ楽にするために作成したAppleScriptです。本来は、すべてのワークフローを自動化するのが筋ですが、限られた時間で限られた範囲の作業のみを自動化するだけでも十分な効果が得られる場合があります。
複数のアプリケーションをまたいでデータのコピー&ペーストを行うようなケースだと、ちょっとした複合作業を自動化することで作業が圧倒的に楽になります。
PDFビューワー「Skim」上でPDFを表示、Numbers上の表にページ数のデータを入れたい。
本AppleScriptを実行すると、
Numbersの選択セルにデータを突っ込んで、選択セルを1つ下に移動させます。
こうして、PDFにTOCをつけてみました(Blogアーカイブ書籍のマイナーバージョンアップ版のため。再ダウンロードでアップデート可能になります)。元データがあるんだから、TOCごと自動で作ってもいいようなものですが、久しぶりだったので(TOC用データを)手で作ってみました。
ここで、意地でもGUI Scriptingを使わないで処理すると、バックグラウンドでも安全に処理できるので大きなメリットがあります。GUI Scriptingだと最前面(フォアグラウンド)でしか操作が保証されていないので、バックグラウンドで操作できたとしてもそれは「ラッキー」でしかありません。
AppleScript名:Skimで表示中のページ数を現在のNumbersのセルに入れて、選択セルを下にひとつ移動 |
— Created 2018-08-18 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" tell application "Skim" tell front document set curPage to index of current page end tell end tell set curPageStr to curPage as string tell application "Numbers" tell front document tell active sheet tell table 1 set mySelectedRanges to properties of selection range set value of cells of selection range to {curPageStr} set selName to name of selection range –選択範囲のrange情報を取得 set {s1, s2} to parseByDelim(selName, ":") of me set s1Row to (address of row of range s1) as integer set colStr to (name of column of range s1) as string set rowStr to (s1Row + 1) as string set adrStr to colStr & rowStr set rangeStr to adrStr & ":" & adrStr try set selection range to range rangeStr on error –Range Over Error from table 1 display dialog "表の範囲をオーバーします" return end try end tell end tell end tell end tell –テキストを指定デリミタでリスト化 on parseByDelim(aData, aDelim) set aText to current application’s NSString’s stringWithString:aData set aList to aText’s componentsSeparatedByString:aDelim return aList as list end parseByDelim |
Keynoteでオープン中の最前面の書類で表示中のスライド(ページ)上の一番左上のshapeオブジェクトの縦横のサイズに他のshapeオブジェクトのサイズを合わせるAppleScriptです。
座標(position)の配列(2D Array)のソートにはBridgePlus Script Libraryを用いています。
▲初期状態
▲本AppleScriptで一番左上のshapeオブジェクトに他のshapeオブジェクトのサイズを合わせた
▲コマンドで適当に上ぞろえや左ぞろえを実行して整列(ここは手作業)
昔、Finder上のアイコンの整列AppleScriptを作ったことがあったので、その処理を使い回せば「いい感じの整列」はできそうです。
AppleScript名:Keynoteで一番左上のshapeオブジェクトに他のshapeのサイズを合わせる |
— Created 2018-08-17 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html tell application "Keynote" tell front document tell (current slide) — すべてのshapeオブジェクトの座標{x,y}を返す set pList to position of every shape — shapeオブジェクトの全座標を昇順ソートして最もX座標値が小さいものを返す set mostLeftPos to first item of sort2DList(pList) of me — 一番X,Y座標値が小さい(=左上にある)オブジェクトを特定 set mostLeftObj to first item of (every shape whose position is equal to mostLeftPos) set mostLeftProp to properties of mostLeftObj –> {opacity:100, parent:slide 13 of document id "95E47D6C-C444-41BD-9E7E-61229486F370" of application "Keynote", class:shape, reflection showing:false, background fill type:color fill, position:{45, 229}, object text:"", width:144, rotation:0, reflection value:0, height:25, locked:false} set mostLeftHeight to height of mostLeftProp set mostLeftWidth to width of mostLeftProp — 「一番左」以外のshapeオブジェクトへの参照を取得して一気にオブジェクトのwidthをそろえる set otherShape to a reference to (every shape whose position is not equal to mostLeftPos) set width of otherShape to mostLeftWidth set height of otherShape to mostLeftHeight end tell end tell end tell on sort2DList(aList) load framework set sortIndexes to {1, 0} –Key Item id: begin from 0 set sortOrders to {true, true} –ascending = true set sortTypes to {"compare:", "compare:"} set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list return resList end sort2DList |
指定のWebページ(URL)の最終更新日時(Last Modified Date)を取得するAppleScriptです。
AppleScriptそのものにWebの最終更新日時を取得する関数や機能はありません。はい、おしまい。
……というのでは、AppleScriptの本質がぜんぜん分かっていないね、ということになります。AppleScriptは「それができるアプリケーション」(など)に依頼を出すのが処理スタイルだからです。
まずは、Safariにコマンドを投げて実行するスタイル。
AppleScript名:Safariのdo javascriptコマンドで最終更新日時を取得 |
— Created 2018-08-17 by Takaaki Naganoya — 2018 Piyomaru Software tell application "Safari" tell front document set dRes to (do JavaScript "document.lastModified;") end tell end tell |
だいたいは、これで手を打つでしょう。ただし、最近のmacOSではセキュリティ強化のためにSafariのdo javascriptコマンドがデフォルトでは禁止されているので、Safariで「開発」メニューを表示させたあとに、「開発」メニューの「AppleEventからのJavaScriptを許可」「スマート検索フィールドからのJavaScriptを許可」を実行しておく必要があります(→ 書籍「AppleScript 10大最新技術」P-84)。
Mac AppStore上で配布/販売するアプリケーションの中で処理することを考えると、SafariをコントロールすることをInfo.plist内で宣言しておけばとくに問題はありません。
do javascriptコマンドの実行で一般的にはファイナルアンサーなのですが、なぜでしょう。リアルタイム日付が返ってくるパターンが多いです。
次は、shellのcurlコマンドを呼び出すスタイルです。指定URLのレスポンスヘッダーを出力させられるので、これを検索して出力します。ただ、YouTubeをはじめとするWebサイトでこの最終更新日を返してこないので、これでもダメな時はダメです。
AppleScript名:curlコマンドで最終更新日時を取得 |
— Created 2018-08-17 by Takaaki Naganoya — 2018 Piyomaru Software tell application "Safari" tell front document set aURL to URL end tell end tell try set uRes to (do shell script "curl -D – -s -o /dev/null " & aURL & " | grep Date:") on error return false end try |
これも現在日時を返してくるパターンが多いですね。また、噂レベルではあるものの「do shell scriptコマンドは極力使わないほうがいいよ」というお達しがScripter界隈で流れているので、将来的に何かがあるのかもしれません(昔の、ごくごく初期のMac OS XはBSDレイヤーというかBSDコマンド類が、OSインストール時にオプション扱いだったので、そういう未来はあるかもしれない)。
Mac AppStore上で配布/販売するアプリケーションの中で処理するのも、とくに問題はないのですが、今度はネットワーク接続することをあらかじめ宣言しておくのと、httpによる通信を行うことを宣言しておかないとネットワーク接続ができません。
最後の手段。Cocoaを呼び出して自前でWebのレスポンスヘッダーを取得するスタイル。
AppleScript名:Cocoaの機能で最終更新日時を取得 |
— Created 2018-08-17 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" property NSString : a reference to current application’s NSString property NSLocale : a reference to current application’s NSLocale property NSURLRequest : a reference to current application’s NSURLRequest property NSDateFormatter : a reference to current application’s NSDateFormatter property NSURLConnection : a reference to current application’s NSURLConnection property NSURLRequestUseProtocolCachePolicy : a reference to current application’s NSURLRequestUseProtocolCachePolicy tell application "Safari" tell front document set aURL to URL end tell end tell set aURL to (current application’s |NSURL|’s URLWithString:aURL) set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me set aDate to headerRes’s |date| as string set lastUpdateDate to dateFromStringWithDateFormat(aDate, "EEE, dd MMM yyyy HH:mm:ss zzz") of me return lastUpdateDate — 指定URLにファイル(画像など)が存在するかチェック –> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)} on checkURLResourceExistence(aURL, timeOutSec as real) set aRequest to (NSURLRequest’s requestWithURL:aURL cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec) set aRes to (NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)) set dRes to (first item of (aRes as list)) set bRes to (second item of (aRes as list)) if bRes is not equal to missing value then set hRes to (bRes’s allHeaderFields()) set aResCode to (bRes’s statusCode()) as integer else set hRes to {} set aResCode to -1 –error end if return {(aResCode = 200), hRes, dRes} end checkURLResourceExistence –指定形式の日付テキストをAppleScriptのdateオブジェクトに変換 on dateFromStringWithDateFormat(dateString, dateFormat) set dStr to NSString’s stringWithString:dateString set dateFormatStr to NSString’s stringWithString:dateFormat set aDateFormatter to NSDateFormatter’s alloc()’s init() aDateFormatter’s setDateFormat:dateFormatStr aDateFormatter’s setLocale:(NSLocale’s alloc()’s initWithLocaleIdentifier:"en_US_POSIX") set aDestDate to (aDateFormatter’s dateFromString:dStr) return aDestDate as date end dateFromStringWithDateFormat |
結果は3つとも変わりませんでした。Cocoa呼び出しするものも、作り置きしておいたサブルーチンを使いまわしただけなので、作るのに3分もかかっていません。
curlを呼び出すスタイル同様、Mac AppStore上で配布/販売するアプリケーションの中で処理するのもとくに問題はないのですが、httpによる通信を行うことを宣言しておかないとネットワーク接続ができません。
Safariでdo javascriptコマンドを実行するものは、最初にdo javascriptコマンドを実行する設定が必要。curlコマンドはまあそんなもんだろうかと。Cocoaの機能を呼び出す方法は、ここまでやってダメならあきらめがつくというところでしょうか。
安全策の上に安全策を講じた、心配性のファイル名称変更AppleScriptです。
基本的にはファイルのリネームプログラムであり、Finder上で選択中のファイルについてリネームを行います。
ただし、ファイルのリネーム時に同じファイル名のファイルがすでに存在していて、普段であれば衝突回避のために子番号を振るなどの対処を行うところが、そういう対応が許されていないようなケースに直面しました。
同じフォルダ内にある3つの画像ファイルを、名称が衝突するような他の名称に変更するようなケースです。
1.jpg --> 2.jpg(すでに2.jpgが存在) 2.jpg --> 3.jpg(すでに3.jpgが存在) 3.jpg --> 1.jpg
少々、頭を抱えてしまいました。
そこで、テンポラリフォルダをファイル1つごとに作成し、テンポラリフォルダにファイルを移動。
1.jpg --> /temp/uuid0001/1.jpg
そのタコツボ的なテンポラリフォルダの中でリネームを実施。
/temp/uuid0001/1.jpg --> /temp/uuid0001/2.jpg
リネームしたファイルを元のフォルダに戻すというリネームルーチンを作成した次第です。もちろん、作成したテンポラリフォルダは削除しておきます。
/temp/uuid0001/2.jpg --> 2.jpg
最近はこのAppleScriptのように、ファイル操作処理はNSFileManagerで実行し、Finderには「選択中のファイルの情報を取得」ぐらいしか行わせていません。そのため、ファイル処理が従来よりもはるかに高速に行えて、ちょっと楽しいぐらいです。
数千ファイルのコピー(込み入ったルールに従いJANコードなどのデータを参照して名前を決定したり、フォルダ名を製品名で作成したりとか)が1・2秒というオーダーで終わってしまうのは、SSD上で処理しているせいもありますが、NSFileManager経由でのファイル処理を行っているおかげです(64bit化したCocoa Finderはとにかく処理が遅いのでAppleScriptからのファイル操作用に使うとダメ)。
AppleScript名:tempフォルダに移動しつつリネームして、終了後に元フォルダに戻す v2.scptd |
— Created 2018-08-14 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" property NSUUID : a reference to current application’s NSUUID property NSString : a reference to current application’s NSString property NSFileManager : a reference to current application’s NSFileManager property tResList : {} property tFol : "" property newNameTemplate : "test_" set tResList to {} set tFol to POSIX path of (path to temporary items) tell application "Finder" set aSel to selection as alias list if aSel = {} then display notification "No Selection" return end if end tell –コピー元ファイルの親フォルダを求める(リネーム後に戻す) set origFol to POSIX path of (contents of first item of aSel) set origFolStr to ((NSString’s stringWithString:origFol)’s stringByDeletingLastPathComponent()) as string –一時フォルダに移動してリネーム set aCount to 1 repeat with i1 in aSel set j1 to POSIX path of (contents of i1) set newName to newNameTemplate & (aCount as string) set j1Res to renameSafely(j1, newName) of me set the end of tResList to (j1Res as string) set aCount to aCount + 1 end repeat –元のフォルダに戻して一時フォルダ削除 repeat with i2 in tResList set j2 to contents of i2 set j2Res to (my moveFileAt:j2 toFolder:origFolStr) set j2Res to deleteParentFol(j2) of me end repeat –安全なリネーム(個別のテンポラリフォルダに移動してリネーム) on renameSafely(aFile, newName) –set tFol to POSIX path of (path to temporary items) set aUUIDstr to ((NSUUID’s UUID()’s UUIDString()) as string) set aTmpFol to tFol & aUUIDstr set dRes to makeDirAbsolutely(aTmpFol) of me set mRes to my moveFileAt:aFile toFolder:aTmpFol set aStr to (NSString’s stringWithString:aFile) set newFullPath to aTmpFol & "/" & aStr’s lastPathComponent() set aExt to aStr’s pathExtension() set fRes to renameFileItem(newFullPath, newName) of me set renamedPath to aTmpFol & "/" & newName & "." & aExt return renamedPath end renameSafely –指定パスににフォルダを作成する on makeDirAbsolutely(dirStr) set fileManager to NSFileManager’s defaultManager() set aRes to fileManager’s createDirectoryAtPath:dirStr withIntermediateDirectories:true attributes:(missing value) |error|:(reference) copy aRes to {aFlag, aError} return aFlag as boolean end makeDirAbsolutely on moveFileAt:POSIXPath toFolder:folderPOSIXPath set POSIXPath to NSString’s stringWithString:POSIXPath set theName to POSIXPath’s lastPathComponent() set folderPOSIXPath to NSString’s stringWithString:folderPOSIXPath set theNewPath to folderPOSIXPath’s stringByAppendingPathComponent:theName set fileManager to NSFileManager’s defaultManager() set theResult to fileManager’s moveItemAtPath:POSIXPath toPath:theNewPath |error|:(missing value) return theNewPath end moveFileAt:toFolder: on renameFileItem(aPOSIXpath as string, newName as string) set theNSFileManager to NSFileManager’s defaultManager() set POSIXPathNSString to NSString’s stringWithString:(aPOSIXpath) –Make New File Path set anExtension to POSIXPathNSString’s pathExtension() set newPath to (POSIXPathNSString’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:newName)’s stringByAppendingPathExtension:anExtension –Rename if theNSFileManager’s fileExistsAtPath:newPath then return false else set theResult to theNSFileManager’s moveItemAtPath:POSIXPathNSString toPath:newPath |error|:(missing value) if (theResult as integer = 1) then return (newPath as string) else return false end if end if end renameFileItem on deleteParentFol(aPOSIX) set aStr to NSString’s stringWithString:aPOSIX set parentFol to aStr’s stringByDeletingLastPathComponent() set aRes to deleteItemAt(parentFol) of me return aRes end deleteParentFol on deleteItemAt(aPOSIX) set theNSFileManager to NSFileManager’s defaultManager() set theResult to theNSFileManager’s removeItemAtPath:(aPOSIX) |error|:(missing value) return (theResult as integer = 1) as boolean end deleteItemAt |
指定のCSVファイルを読み込んで、表インタフェースで表示確認するAppleScriptです。
Numbers.appから文字コードにUTF-8を指定して書き出したCSVファイルを想定しています。CSVデータのparseにはBridgePlusを、表UIの表示にはMyriad Tables Libを利用しています。
ほぼ、テンプレートともいえるような定型処理ですが、巨大なAppleScriptに入れたときにはusing terms from句がないとアプレットとして動作中にクラッシュが発生する可能性があります。
CSVデータの処理はありふれたありきたりの内容で、本来は処理内容にはほとんど関係ない表示・確認を行わせるとユーザーの反応がいいようです。
あとは、CSVデータ中にカラムヘッダーが存在するかどうかをCSVデータからでは検出できないことが問題です。せめて、メタデータ中にでもカラムヘッダーの有無を(Export時に)書いておいてくれるとよいのですが。あとは、CSV1行目のデータと他の行との文字コードの分布状況を比較して統計処理すると、相関性を数値化してヘッダーかどうかを計算できそうですが、、、そこまでやるのは「やりすぎ」でしょう。
ユーザーにヘッダーの有無を確認するとか、そのぐらい?
_kMDItemOwnerUserID = 504 kMDItemAlternateNames = ( "2010_index.csv" ) kMDItemContentCreationDate = 2018-08-14 00:51:24 +0000 kMDItemContentModificationDate = 2018-08-14 00:51:24 +0000 kMDItemContentType = "public.comma-separated-values-text" kMDItemContentTypeTree = ( "public.comma-separated-values-text", "public.data", "public.delimited-values-text", "public.plain-text", "public.item", "public.content", "public.text" ) kMDItemDateAdded = 2018-08-14 00:51:24 +0000 kMDItemDisplayName = "2010_index" kMDItemFSContentChangeDate = 2018-08-14 00:51:24 +0000 kMDItemFSCreationDate = 2018-08-14 00:51:24 +0000 kMDItemFSCreatorCode = "" kMDItemFSFinderFlags = 16 kMDItemFSHasCustomIcon = (null) kMDItemFSInvisible = 0 kMDItemFSIsExtensionHidden = 1 kMDItemFSIsStationery = (null) kMDItemFSLabel = 0 kMDItemFSName = "2010_index.csv" kMDItemFSNodeCount = (null) kMDItemFSOwnerGroupID = 80 kMDItemFSOwnerUserID = 504 kMDItemFSSize = 7746 kMDItemFSTypeCode = "" kMDItemKind = "Comma Separated Spreadsheet (.csv)" kMDItemLogicalSize = 7746 kMDItemPhysicalSize = 8192 kMDItemUserCreatedDate = ( "2018-08-14 00:51:24 +0000" ) kMDItemUserCreatedUserHandle = ( 504 )
▲ここ(メタデータ)にCSVのヘッダーの有無の情報が入っているといいのに、、、、にしても、メタデータに大昔のResource Forkよろしくいろんなデータが突っ込まれるようになってきました
▲CSVファイルの選択
▲CSVファイル内容の表示
▲CSV内のカラム数が合っていた場合の動作
▲CSV内のカラム数が合っていない場合の動作(データ確認表示)
▲CSV内のカラム数が合っていない場合の動作(ダイアログ表示)
AppleScript名:CSVデータを読み込んで表インタフェースで表示確認 v2.scptd |
— – Created by: Takaaki Naganoya – Created on: 2018/08/10 — – Copyright © 2018 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "AppKit" use Bplus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html use tableLib : script "Myriad Tables Lib" –https://www.macosxautomation.com/applescript/apps/Script_Libs.html#MyriadTablesLib –Numbersから書き出したUTF-8のCSVを読み取る set someString to read (choose file of type {"public.comma-separated-values-text"}) as «class utf8» –読み込んだテキストをParseして、ヘッダーとデータ本体に分離 set {theHeader, origList} to readCSVAndParse(someString) of me –Table UIを表示して内容を確認 set colMax to 3 –あらかじめ期待しているカラム数 set {dRes, tRes} to dispTableUI(theHeader, origList, colMax) of me if dRes = false then display dialog "データ不全のため処理を終了します" with title "CSVデータエラー" return else display dialog "CSVデータの処理を続行します" with title "CSVデータOK" end if on readCSVAndParse(someString) load framework using terms from script "BridgePlus" set theData to current application’s SMSForder’s arrayFromCSV:someString commaIs:"," set origList to (current application’s SMSForder’s subarraysIn:theData paddedWith:"" |error|:(missing value)) as list end using terms from set theHeader to contents of first item of origList –Header Row set origList to rest of origList –Data return {theHeader, origList} end readCSVAndParse on dispTableUI(theHeader, origList, colMax) set {curDispW, curDispH} to (current application’s NSScreen’s mainScreen()’s frame()’s |size|()) as list set curLen to length of first item of origList set dRes to (curLen = colMax) as boolean if dRes = true then set aMessage to "問題がなければ「OK」をクリックして実行してください" –Normal message set aTitle to "CSVファイルの内容確認" else set aMessage to "CSVファイルのカラム数が期待どおりではありません。「Cancel」をクリックして実行を停止してください" –Error Message set aTitle to "CSVファイルに異常あり" end if using terms from script "Myriad Tables Lib" tell script "Myriad Tables Lib" set aDispBounds to my makeInitialBounds:(curDispW – 100) withHeight:(curDispH – 100) set theTable to make new table with data origList column headings theHeader with title aTitle with prompt aMessage with row numbering, empty selection allowed and multiple lines allowed modify table theTable initial position aDispBounds –表を表示 set tRes to (display table theTable) return {dRes, tRes} end tell end using terms from end dispTableUI on makeInitialBounds:(aTWidth as integer) withHeight:(aTHeight as integer) set aDispBounds to current application’s NSScreen’s mainScreen()’s frame() set aWidth to (width of |size| of aDispBounds) set aHeight to (height of |size| of aDispBounds) set xPos to (aWidth div 2) – (aTWidth div 2) set yPos to (aHeight div 2) – (aTHeight div 2) return {xPos, yPos, aTWidth, aTHeight} end makeInitialBounds:withHeight: |
込み入った案件で必要になって、Script Debugger 7を購入しました。Adobeの大型アプリケーションのオブジェクト階層を調査したい場合にはScript Debuggerを使わないのは自殺行為です。
その反面、Xcodeの外部エディタとして使うなら、まだASObjC Explorer 4のほうが使い勝手がいいし(Xcode上で外部エディタを指定してscriptを再編集するとxibファイルのトラブルなど諸問題が解決するのに、Script Debuggerだと解決しない)、クラッシュの頻度もややScript Debuggerのほうが高い気がします。
本気で組んだ仕事用のAppleScript相手だと、Script Debugger上で作って、Script Debugger上で動作させるだけなら問題はないのですが、アプレットを書き出して実行させるあたりで疑問符が出てきます。
AppleScript上で何かの予約語が存在していた場合に、「どのアプリケーション、どのScript Libraries」の用語辞書から使用したものなのかをusing terms from句で囲ってこまめに宣言しておかないと、書き出したAppleScriptアプレットを起動しようとしてもクラッシュします。
# しかも、Scriptのサイズが小さい場合にはクラッシュせず、大きくなるとクラッシュするという、、、
また、using terms from句で囲むさいにはuse宣言で代入したプロパティが使えず(using terms from bPlus とか)、using terms from script “BridgePlus”のように書かないとScript Debugger上で構文確認が通りません(こちらはAppleScript処理系そのものの動作の癖?)。
さらに、「anything」(≒”any”)という(Appleに)いい加減に実装されてきた予約語を、さまざまなライブラリで別々に(ちゃんと)実装していたりすると、Script Debugger上での構文確認時に「どのアプリケーションの予約語なのか分からないぞ」というワーニングメッセージが出ます。これ、ダイアログで表示するなら、リストから選択させるとかいった動作の方がいいと思います。
このあたり、Apple純正のスクリプトエディタやASOBjC Explorer 4(現在はScript Debuggerと合流)では確認していない現象ですが、Script Debuggerを使っているかぎりはこまめにusing terms fromで囲う必要があるようです。
Adobe Illustrator CC 2018(バージョン22.1)で、複数のアートボードを持つAdobe Illustrator書類をアートボードごとに分割保存するAppleScriptです。
オプションの指定方法にものすごく納得しづらいものを感じるものの、これで動くので仕方ないだろうかというところ。
AppleScript名:複数のアートボードを持つAI書類を、デスクトップにアートボードごとに分割して保存.scptd |
use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use scripting additions set dtPath to ((path to desktop) as string) & "template.ai" tell application "Adobe Illustrator" tell front document set aCount to (count every artboard) as string set sOpt to {class:Illustrator save options, save multiple artboards:true, artboard range:"1-" & aCount} save as Illustrator in file dtPath with options sOpt end tell end tell |
Adobe Illustrator CC 2018(バージョン22.1)で、オープン中の(複数のアートボードを持つ)書類を、アートボード単位で別書類に分けるAppleScriptの改修版です。
書類は(とりあえず)デスクトップに書き込まれます。前バージョンのように、アートボード外のオブジェクトが残るといったことはありません。
「exportSelectedArtwork」という長いコマンドを見つけて、ためしてみたら期待どおりの挙動であったために使ってみました。
……すると、なおのことAppleScriptからartworkのselectができないのか?(実行すると100% Illustratorがクラッシュする) artworkからnameの取得ができないのか?(実行するとエラーになる)というあたりが謎です。
artboardも選択状態になっている(はず)だと思って、そのまま実行してみたところ、artboardのサイズでは書き出されていないようです。少し別の方法を試す必要があり、自分は前バージョンのScriptにもどって作業を行っています。
AppleScript名:オープン中のIllustrator書類をartboardごとに分割 v3.scptd |
— – Created by: Takaaki Naganoya – Created on: 2018/08/07 – Modified on: 2018/08/09 – Copyright © 2018 Piyomaru Software, All Rights Reserved — use AppleScript version "2.5" — El Capitan (10.11) or later use framework "Foundation" use scripting additions set dPath to (path to desktop) as string set namesList to {} tell application "Adobe Illustrator" tell front document set aCount to count every artboard set aPath to (file path) as string end tell repeat with i from 1 to aCount set aName to getArtboardName(i – 1) of me –index base correction (1 –> 0) set outPath to dPath & aName & ".ai" selectArtboard(i – 1) of me –index base correction (1 –> 0) tell front document selectobjectsonactiveartboard exportSelectedArtwork to file outPath end tell end repeat end tell –https://lists.apple.com/archives/applescript-users/2015/Jul/msg00104.html on selectArtboard(theArtboardIndex as string) tell application "Adobe Illustrator" tell front document –Index is 0 based do javascript ("app.activeDocument.artboards.setActiveArtboardIndex(" & theArtboardIndex & ")") end tell end tell end selectArtboard on getArtboardName(theArtboardIndex as string) tell application "Adobe Illustrator" tell front document –Index is 0 based do javascript ("app.activeDocument.artboards[" & theArtboardIndex & "].name") end tell end tell end getArtboardName |
Adobe Illustrator CC 2018(バージョン22.1)で、オープン中の(複数のアートボードを持つ)書類を、アートボード単位で別書類に分けるAppleScriptです。
書類は(とりあえず)デスクトップに書き込まれます。
こういうコマンドが最初からあってもおかしくないところですが、存在していないようなので書いてみました。まずは、オープン中の書類のパスを取得し、各アートボードの名称を取得して書き出し時のファイル名として使用します。
元ファイルをクローズしては、デスクトップに新規名称でコピーし、コピーした書類をオープン、書き出し対象外のアートボードを順次選択してはアートボード上のオブジェクトを削除してアートボード自体も削除、すべて削除したら保存してクローズという動作を行います。
本来であれば高速化のために複数のアートボードを一括で削除することを考えるところですが、アートボードの削除だけであればできるものの、アートボード上のオブジェクトの削除が行えなくなってしまうので、順次削除に落ち着きました。また、本Scriptが本気でスピードを求められるようなハードな用途ではなく、文字や画像の流し込みテンプレートのメンテナンス用という「平和な用途」のために作成したものであるため、高速化処理については考慮していません。
最初からアートボード外に置かれたオブジェクトについては削除していません。あくまで自分用のツールなので作り込む必要性を感じません。気が向いたら削除するのではないでしょうか。
本Scriptの作成中にも、単にAppleScriptで命令を出しただけで派手にクラッシュしまくるAdobe Illustrator。バージョンが上がって見た目が変わっても、あの出来の悪いIllustratorであることに変わりはないのだと思い知らされました。
AppleScript名:オープン中のIllustrator書類をartboardごとに分割 v2.scptd |
— – Created by: Takaaki Naganoya – Created on: 2018/08/07 — – Copyright © 2018 Piyomaru Software, All Rights Reserved — use AppleScript version "2.5" — El Capitan (10.11) or later use framework "Foundation" use scripting additions set dPath to (path to desktop) as string set namesList to {} tell application "Adobe Illustrator" tell front document set aCount to count every artboard set aPath to (file path) as string end tell repeat with i from 1 to aCount set aName to getArtboardName(i – 1) of me –index base correction (1 –> 0) set the end of namesList to aName end repeat close every document saving no end tell repeat with i from 1 to aCount set aRes to retSerialListWithExclusion(1, aCount, i) of me set aaRes to reverse of aRes –artboardをIndex値で指定するため、先頭から削除するとIndex値が変わる。そのため逆順に処理 set tmpName to contents of item i of namesList set fromFile to POSIX path of aPath set fromFilePOSIX to quoted form of POSIX path of fromFile set toFile to (dPath & tmpName & ".ai") set toFilePOSIX to quoted form of POSIX path of toFile set shText to "cp " & fromFilePOSIX & " " & toFilePOSIX do shell script shText tell application "Adobe Illustrator" open file toFile tell front document repeat with ii in aaRes set jj to ii as integer selectArtboard(jj – 1) of me –index base correction (1 –> 0) selectobjectsonactiveartboard delete selection delete artboard jj end repeat close with saving end tell end tell end repeat –https://lists.apple.com/archives/applescript-users/2015/Jul/msg00104.html on selectArtboard(theArtboardIndex as string) tell application "Adobe Illustrator" tell front document –Index is 0 based do javascript ("app.activeDocument.artboards.setActiveArtboardIndex(" & theArtboardIndex & ")") end tell end tell end selectArtboard on getArtboardName(theArtboardIndex as string) tell application "Adobe Illustrator" tell front document –Index is 0 based do javascript ("app.activeDocument.artboards[" & theArtboardIndex & "].name") end tell end tell end getArtboardName on retSerialListWithExclusion(aFromNum as integer, aToNum as integer, anEXnum as integer) set outList to {} repeat with i from aFromNum to aToNum if i = anEXnum then — else set the end of outList to i end if end repeat return outList end retSerialListWithExclusion |
本BlogのようなWordPressで運用されており、AppleScriptのURLリンクを記事に埋め込んでいるWordPressに対して、XML-RPC経由で指定IDの記事本文を取得し、埋め込まれているURLリンクからAppleScriptのソースコードを取得して、メモリー上でコンパイルして書式つきテキストに変換し、AppleScript構文書式をもとにpropertyラベルを抽出、そのうちCocoa Classのみをリストで取得するAppleScriptです。
本Blogに投稿した記事から宣言しているCocoa Classを抽出し、自動でタグ付けするために準備したものです。1記事に対して複数のAppleScriptが掲載されている場合にも対応しています。
HTMLReader.frameworkを用いてBlog本文からのリンク抽出、リンクURL抽出を行っています。
–> HTMLReader.framework(To ~/Library/Frameworks/)
本Sample Scriptで指定したIDの記事のproperty部分はこのようになっており、
本Scriptの実行によって抽出されたCocoa Class(単なる変数的なproperty項目や、Enumは対象外のため排除)は、
–> {“NSString”, “NSArray”, “OSAScript”, “NSPredicate”, “OSALanguage”, “NSDictionary”, “OSALanguageInstance”, “NSBundle”, “NSUnarchiver”}
のようになります。自分の環境でMacBook Proを有線ネットワーク接続した状態で、3.3〜3.4秒程度かかっています。大量の記事を処理する場合には本AppleScriptの並列処理を行うと処理時間の大幅な短縮が期待できます(MacのCPUがサーマルスロットリングで速度低下しなければ)。
また、負荷が集中している特定コアの動作周波数を上げ、他のコアの動作周波数を落とすTurbo Boostが有効な状態で並列処理を実行すると、並列処理を行う意義そのものが低下してしまうため、Turbo-Boost-Switcherのようなツールの併用が必要と思われます。
AppleScript名:WordPressの指定IDの記事にリンクされているapplescriptからCocoa Classのproperty宣言を抽出 v3 |
— Created 2018-07-30 by Takaaki Naganoya — Modified 2018-08-05 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "AppKit" use framework "OSAKit" use framework "HTMLReader" –https://github.com/nolanw/HTMLReader property |NSURL| : a reference to current application’s |NSURL| property NSArray : a reference to current application’s NSArray property NSString : a reference to current application’s NSString property NSBundle : a reference to current application’s NSBundle property NSThread : a reference to current application’s NSThread property OSAScript : a reference to current application’s OSAScript property NSPredicate : a reference to current application’s NSPredicate property NSTextView : a reference to current application’s NSTextView property NSDictionary : a reference to current application’s NSDictionary property NSUnarchiver : a reference to current application’s NSUnarchiver property OSALanguage : a reference to current application’s OSALanguage property OSAScriptView : a reference to current application’s OSAScriptView property NSMutableArray : a reference to current application’s NSMutableArray property OSAScriptController : a reference to current application’s OSAScriptController property NSMutableDictionary : a reference to current application’s NSMutableDictionary property OSALanguageInstance : a reference to current application’s OSALanguageInstance property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding set postID to 3864 set {myUser, myPass} to getAcountData() of me set aURL to "http://piyocast.com/as/xmlrpc.php" set cocoaClassList to getCocoaPropListFromPost(aURL, postID, myUser, myPass) of me –> {"NSString", "NSBundle", "NSPredicate", "NSDictionary", "NSMutableArray", "NSMutableDictionary"} –指定Blogの指定IDの記事にURLリンクされているAppleScriptから、Cocoa Classのpropertyのみ取得する on getCocoaPropListFromPost(aURL, postID, myUser, myPass) –AppleScriptの構文色分け設定ファイルを読み込んで、重複色のチェックを実施 set cList to getAppleScriptSourceColors() of me set cRes to chkASLexicalFormatColorConfliction(cList) of me –構文色分けの重複色チェック if cRes = false then error "There is some duplicate(s) color among AppleScript’s lexical color settings" –WordPressの指定Post IDの記事を取得してリンクされているURLからURL Schemeでフィルタして、リンクされているAppleScriptのソースを取得 set aScheme to "applescript://" set sourceList to getASSouceLinkedInWordPressPost(postID, aURL, aScheme, myUser, myPass) of me –AppleScriptのソースをRAM上でコンパイル(構文確認)して、構文色分けしたRTFを取得。RTFの書式情報をparseしてattribute runsと同様のrecordを生成 –構文色分けをもとにproperty項目を抽出し、Cocoa Classに該当するもののみを抽出 set outList to {} repeat with i in sourceList set j to contents of i set anAttrStr to compileASandReturnAttributedString(j) of me set attrRes to getAttributeRunsFromAttrString(anAttrStr) of me set propNames to getPropertyNamesCocoaOnly(cList, attrRes) of me if propNames is not equal to {} then set outList to outList & propNames end if end repeat –1D Listのユニーク化(重複要素の排除) set aArray to NSArray’s arrayWithArray:outList set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self" set bList to bArray as list of string or string –as anything return bList end getCocoaPropListFromPost –Property名称を取得する on getPropertyNamesCocoaOnly(cList, aRec) script spdHnd property aRec : {} end script set (aRec of spdHnd) to aRec set targAttr to contents of item 7 of cList –ハンドラあるいは変数 set tmpCoStr to ((redValue of targAttr) as string) & " " & ((greenValue of targAttr) as string) & " " & ((blueValue of targAttr) as string) set ontoColItem to contents of item 3 of cList –スクリプティング予約語(on/to) set ontoCoStr to ((redValue of ontoColItem) as string) & " " & ((greenValue of ontoColItem) as string) & " " & ((blueValue of ontoColItem) as string) –変数あるいはハンドラ名称をリストアップ(variables & handler) set tmp1Array to NSArray’s arrayWithArray:(aRec of spdHnd) set thePred0 to NSPredicate’s predicateWithFormat_("colorStr == %@", tmpCoStr) set dArray to (tmp1Array’s filteredArrayUsingPredicate:thePred0) as list –改行を含むデータをリストアップ(text data contains return) set thePred1 to NSPredicate’s predicateWithFormat_("stringVal CONTAINS %@", return) set eArray to ((tmp1Array’s filteredArrayUsingPredicate:thePred1)’s valueForKeyPath:"itemIndex") as list set the beginning of eArray to 0 –ハンドラ宣言部がTopに来る場合に備える –"property"(プロパティ宣言)の項目をリストアップ 文字と色で抽出 set thePred2 to NSPredicate’s predicateWithFormat_("stringVal == %@ && colorStr == %@ ", "property", ontoCoStr) set fArray to ((tmp1Array’s filteredArrayUsingPredicate:thePred2)’s valueForKeyPath:"itemIndex") as list set handlerList to {} –property ではじまるハンドラの抽出 repeat with i in eArray –改行を含むテキストのアイテム番号リスト set j to (contents of i) as integer repeat with ii in fArray –"on"の項目リスト set jj to (contents of ii) as integer set handlerStr to missing value if (j + 1) = jj then set handlerStr to stringVal of (item (jj + 2) of ((aRec of spdHnd) as list)) else if (j + 2) = jj then set handlerStr to stringVal of (item (jj + 2) of ((aRec of spdHnd) as list)) end if set tmpStr to repChar(handlerStr, "|", "") of me if tmpStr is not in {"error", missing value} and tmpStr is not in handlerList then –抽出したProperty宣言がCocoa Classのものかどうか判定 if searchClassInFrameworks(tmpStr) of me is not equal to false then set the end of handlerList to tmpStr end if end if end repeat end repeat return handlerList end getPropertyNamesCocoaOnly –RAM上にスクリプトエディタと同じ部品を組み立て(非表示)、AppleScriptのソーステキストからObjectを生成し、Attributed Stringデータを返す on compileASandReturnAttributedString(theSource as string) set targX to 1024 –View Width set targY to 2048 –View Height set osaCon to current application’s OSAScriptController’s alloc()’s init() set osaView to current application’s OSAScriptView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY)) set resView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY)) resView’s setRichText:true resView’s useAllLigatures:true osaCon’s setScriptView:osaView osaCon’s setLanguage:(OSALanguage’s languageForName:"AppleScript") osaCon’s setResultView:resView osaView’s setString:theSource osaCon’s compileScript:(missing value) –Compile(構文確認) set aRes to (osaView’s attributedString()) return aRes end compileASandReturnAttributedString –Attributed StringをDictionary化 on getAttributeRunsFromAttrString(theStyledText) script aSpd property styleList : {} end script set (styleList of aSpd) to {} —for output set thePureString to theStyledText’s |string|() –pure string from theStyledText set theLength to theStyledText’s |length|() set startIndex to 0 set itemCount to 1 repeat until (startIndex = theLength) set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(reference) inRange:{startIndex, theLength – startIndex} –String set aText to (thePureString’s substringWithRange:theRange) as string –Color set aColor to (theAtts’s valueForKeyPath:"NSColor") if aColor is not equal to missing value then set aSpace to aColor’s colorSpace() set aRed to (aColor’s redComponent()) * 255 set aGreen to (aColor’s greenComponent()) * 255 set aBlue to (aColor’s blueComponent()) * 255 set colList to {aRed as integer, aGreen as integer, aBlue as integer} –for comparison set colStrForFind to (aRed as integer as string) & " " & (aGreen as integer as string) & " " & (aBlue as integer as string) –for filtering else set colList to {0, 0, 0} set colStrForFind to "0 0 0" end if –Font set aFont to (theAtts’s valueForKeyPath:"NSFont") if aFont is not equal to missing value then set aDFontName to aFont’s displayName() set aDFontSize to aFont’s pointSize() end if set the end of (styleList of aSpd) to {stringVal:aText, colorStr:colStrForFind, colorVal:colList, fontName:aDFontName as string, fontSize:aDFontSize, itemIndex:itemCount} set startIndex to current application’s NSMaxRange(theRange) set itemCount to itemCount + 1 end repeat return (styleList of aSpd) end getAttributeRunsFromAttrString –指定クラスがいずれかのCocoa Frameworkに所属しているかを検索 on searchClassInFrameworks(aTarget) set aClass to current application’s NSClassFromString(aTarget) if aClass = missing value then return false set theComponenents to (NSBundle’s bundleForClass:aClass)’s bundleURL’s pathComponents() set thePred to NSPredicate’s predicateWithFormat:"pathExtension == ’framework’" set aRes to (theComponenents’s filteredArrayUsingPredicate:thePred)’s firstObject() as list of string or string return aRes end searchClassInFrameworks –指定Post IDのWordPress記事から、指定SchemeのURLを抽出し、AS Sourceをdecodeしてproperty行のみ抽出 on getASSouceLinkedInWordPressPost(postID, aURL, aScheme, myUser, myPass) –call xmlrpc命令に対するURLの間接指定を有効にするために、AppleScriptの構文解釈機能をダミーURLでだます using terms from application "http://piyocast.com/as/xmlrpc.php" –URLと判定されればなんでもいい tell application aURL set wRes to (call xmlrpc {method name:"wp.getPost", parameters:{"1", myUser, myPass, postID as string}}) end tell end using terms from set aBody to post_content of wRes –Blog本文 –記事中でリンクしているURLを取得し、指定のURL Schemeでフィルタする set urlList to filterURLLinksByScheme(aBody, aScheme) of me set propList to {} repeat with i in urlList set j to contents of i set urlRec to parseQueryDictFromURLString(j) of me set tmpScript to (urlRec’s |script|) as string –Get AppleScript Source set propList to propList & tmpScript end repeat return propList end getASSouceLinkedInWordPressPost on parseQueryDictFromURLString(aURLStr as string) if aURLStr = "" then error "No URL String" set aURL to |NSURL|’s URLWithString:aURLStr set aQuery to aURL’s query() –Get Query string part from URL if aQuery’s |length|() = 0 then return false set aDict to NSMutableDictionary’s alloc()’s init() set aParamList to (aQuery’s componentsSeparatedByString:"&") as list repeat with i in aParamList set j to contents of i if length of j > 0 then set tmpStr to (NSString’s stringWithString:j) set eList to (tmpStr’s componentsSeparatedByString:"=") set anElement to (eList’s firstObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding)) set aValStr to (eList’s lastObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding)) (aDict’s setObject:aValStr forKey:anElement) end if end repeat return aDict end parseQueryDictFromURLString –指定のHTML文字列から、Link URLを抽出し、schemeで再抽出する on filterURLLinksByScheme(aBody, aScheme) set conType to "text/html" –HTML文字列をいったんNSDataにしているのは、HTMLReader.frameworkの仕様のため set aData to (current application’s NSString’s stringWithString:aBody)’s dataUsingEncoding:(current application’s NSUTF8StringEncoding) set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType set aTextArray to ((aHTML’s nodesMatchingSelector:"a")’s textContent) as list –リンク文字 set aLinkList to ((aHTML’s nodesMatchingSelector:"a")’s attributes’s valueForKeyPath:"href") as list –URL文字列 set outList to {} repeat with i in aLinkList set j to contents of i if j begins with aScheme then set the end of outList to j end if end repeat return outList end filterURLLinksByScheme –文字置換 on repChar(origText as string, targChar as string, repChar as string) set curDelim to AppleScript’s text item delimiters set AppleScript’s text item delimiters to targChar set tmpList to text items of origText set AppleScript’s text item delimiters to repChar set retText to tmpList as string set AppleScript’s text item delimiters to curDelim return retText end repChar –AppleScriptの構文色分けのカラー値をRGBで取得する on getAppleScriptSourceColors() — get the plist info as a dictionary set thePath to NSString’s stringWithString:"~/Library/Preferences/com.apple.applescript.plist" set thePath to thePath’s stringByExpandingTildeInPath() set theInfo to NSDictionary’s dictionaryWithContentsOfFile:thePath — extract relevant part and loop through set theArray to (theInfo’s valueForKey:"AppleScriptSourceAttributes") as list set colList to {} repeat with i from 1 to count of theArray set anEntry to item i of theArray set colorData to NSColor of anEntry set theColor to (NSUnarchiver’s unarchiveObjectWithData:colorData) set {rVal, gVal, bVal} to retColListFromNSColor(theColor, 255) of me set fontData to NSFont of anEntry set theFont to (NSUnarchiver’s unarchiveObjectWithData:fontData) set aFontName to theFont’s displayName() as text set aFontSize to theFont’s pointSize() set aColRec to {redValue:rVal, greenValue:gVal, blueValue:bVal, fontName:aFontName, fontSize:aFontSize} set the end of colList to aColRec end repeat return colList end getAppleScriptSourceColors –NSColorからRGBの値を取り出す on retColListFromNSColor(aCol, aMAX as integer) set aRed to round ((aCol’s redComponent()) * aMAX) rounding as taught in school set aGreen to round ((aCol’s greenComponent()) * aMAX) rounding as taught in school set aBlue to round ((aCol’s blueComponent()) * aMAX) rounding as taught in school if aRed > aMAX then set aRed to aMAX if aGreen > aMAX then set aGreen to aMAX if aBlue > aMAX then set aBlue to aMAX return {aRed, aGreen, aBlue} end retColListFromNSColor –AS書式で配色に重複がないかどうかチェック on chkASLexicalFormatColorConfliction(aList) set anArray to current application’s NSArray’s arrayWithArray:aList set bList to (anArray’s valueForKeyPath:"redValue.stringValue") as list set cList to (anArray’s valueForKeyPath:"greenValue.stringValue") as list set dList to (anArray’s valueForKeyPath:"blueValue.stringValue") as list set colStrList to {} repeat with i from 1 to (length of bList) set bItem to contents of item i of bList set cItem to contents of item i of cList set dItem to contents of item i of dList set the end of colStrList to bItem & " " & cItem & " " & dItem end repeat set aRes to returnDuplicatesOnly(colStrList) of me if aRes is equal to {} then return true –重複が存在しなかった場合 else return false –重複があった場合 end if end chkASLexicalFormatColorConfliction on returnDuplicatesOnly(aList as list) set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList set bList to (aSet’s allObjects()) as list set dupList to {} repeat with i in bList set aRes to (aSet’s countForObject:i) if aRes > 1 then set the end of dupList to (contents of i) end if end repeat return dupList end returnDuplicatesOnly on getAcountData() return {"xxxxxxxx_xx", "XXXXXXXXXXXXXXXXXXXXXXXX"} –user name, password end getAcountData |
Google Drive File Stream上でSpotlight Indexを生成するAppleScriptです。
Google Driveには、Gmailのオマケでついてくる個人のデバイス間で情報のシンクロを行う目的のPersonal版(DropboxのGoogle版的なもの)と、企業内やグループ内でファイル共有を行うG SuiteのGoodle Drive File Streamがあり、後者についてはGoogleによる「Google Drive File Stream」ツールが提供され、GoogleDriveをデスクトップにマウントできます。
実際にGoogle Drive File Streamを使ってみたところ、デフォルト状態だとSpotlight検索が効きませんでした。
そこで、Index生成してみたものの…既存のファイルについてはSpotlight検索できませんでした。Index生成後に、新規追加したファイルについてはSpotlight検索が有効でした。
後日談:ただ、Google Drive File Streamに対してSpotlight Indexを常時生成するようにすると、CPUに対する負荷が大きくなるため、実際の運用ではSpotlightを無効にして運用するようにしました。
AppleScript名:Google Drive File Streamでインデックス生成 |
do shell script "mdutil -E /Volumes/GoogleDrive/" with administrator privileges |
自分が試したのは、Google Drive File Stream内の「マイドライブ」領域のみで、
「グループドライブ」領域についてはまだテストできていません。
既存のファイルについては、mdimportコマンドでインデックス作成すれば大丈夫だと思っていますが、実際に試してみると予想外の挙動を行うのかもしれません。
AppleScript名:GDFS上でSpotlight検索 |
— Created 2016-06-17 by Takaaki Naganoya — 2016 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" property NSArray : a reference to current application’s NSArray property NSPredicate : a reference to current application’s NSPredicate property NSSortDescriptor : a reference to current application’s NSSortDescriptor property NSMetadataQuery : a reference to current application’s NSMetadataQuery property NSNotificationCenter : a reference to current application’s NSNotificationCenter property NSMetadataQueryDidUpdateNotification : a reference to current application’s NSMetadataQueryDidUpdateNotification property NSMetadataQueryDidFinishGatheringNotification : a reference to current application’s NSMetadataQueryDidFinishGatheringNotification property searchRes : {} set origPath to POSIX path of (choose folder) set aList to spotlightSearch(origPath, "kMDItemContentType == ’public.png’") of me on spotlightSearch(origPath, aMDfindParam) set my searchRes to {} –initialize initiateSearchForFullPath(aMDfindParam, origPath) –Predicate & Scope Directory –Waiting for the result repeat while my searchRes = {} delay 0.01 end repeat set anObj to my searchRes’s firstObject() –Pick up the first one for test if anObj = missing value then return {} –No Result set resArray to {} repeat with anItem in my searchRes set j to contents of anItem set aPath to (j’s valueForAttribute:"kMDItemPath") as string set the end of resArray to aPath end repeat return resArray end spotlightSearch on initiateSearchForFullPath(aQueryStrings, origPath) set aSearch to NSMetadataQuery’s alloc()’s init() NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:"queryDidUpdate:" |name|:(NSMetadataQueryDidUpdateNotification) object:aSearch NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:"initalGatherComplete:" |name|:(NSMetadataQueryDidFinishGatheringNotification) object:aSearch set aPredicate to NSPredicate’s predicateWithFormat:aQueryStrings aSearch’s setPredicate:aPredicate set aScope to NSArray’s arrayWithObjects:{origPath} aSearch’s setSearchScopes:aScope set sortKeys to NSSortDescriptor’s sortDescriptorWithKey:"kMDItemFSName" ascending:true aSearch’s setSortDescriptors:(NSArray’s arrayWithObject:sortKeys) aSearch’s startQuery() end initiateSearchForFullPath on queryDidUpdate:sender log sender –> (NSConcreteNotification) NSConcreteNotification 0x7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = <NSMetadataQuery: 0x7fa6172c6ca0>} end queryDidUpdate: on initalGatherComplete:sender set anObject to sender’s object anObject’s stopQuery() NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(NSMetadataQueryDidUpdateNotification) object:anObject NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(NSMetadataQueryDidFinishGatheringNotification) object:anObject set my searchRes to anObject’s results() end initalGatherComplete: |
WordPressで稼働しているBlog(AppleScriptの穴)の、指定記事IDの本文を取得し、本文内でリンクしているURLのうち指定schemeのものを抽出し、URLリンクされているAppleScriptのソースをデコードしてproperty宣言文のうちCocoa Classの宣言を行っているものを抽出するAppleScriptです。
HTMLReader.frameworkを用いてBlog本文からのリンク抽出、リンクURL抽出を行っています。
–> HTMLReader.framework(To ~/Library/Frameworks/)
本Blogで、Tagの運用を変更しようと思い立ち、手作業で修正をはじめました。アプリケーション名のほかにCocoa Class名をTagにしようという試みです。
ただ、数個の記事のTag付け直しを行っただけで「手作業では終わらない」ことが判明。2月に再構築をはじめて1,000本ぐらいの記事をアップしているので、手作業ではとても無理です
本Blogの記事にURLリンクされているAppleScriptソースプログラムを(XML-RPC経由で)AppleScriptから自動で取得し、
その中のproperty宣言文を抽出して、
Cocoa Classの宣言文のみをリストアップして、
Blog記事のTagに自動で指定できないか、と試してみたものです。
現時点で、
(1)指定Blog記事の本文を取得
(2)(1)から指定URL SchemeのリンクURLを抽出
(3)(2)のURL EncodeされているAppleScriptソースをDecode
(4)property宣言文のみ抽出
(5)property宣言ラベルがCocoa Classのものかをチェックして抽出
というところまでできています。本プログラムは、BlogのUser NameとPasswordが必要なので、リストのまま動かしてもエラーになり動作しません。同様にWordPressで運用されているBlogがあれば、そちらで試してみるのもよいでしょう。
XML-RPCでWordPressと通信するのには、記事アップロード自動化に使ったFrameworkもありますが、ためしにAppleScript標準搭載のcall xmlrpcコマンドを使ってみました。記事新規投稿コマンドだとクラッシュを起こしますが、この程度の用途ならクラッシュせずに使えるようです。
また、property文の抽出は構文要素を考慮していないため、コメントアウトされているものも拾ってくる可能性があります(単に行頭のproperty宣言文を拾っているだけなので、複数行コメントになっているものは拾われてくることでしょう)。
(*
property NSArray: a reference to current application’s NSArray
property NSString: a reference to current application’s NSString
*)
これを防ぐために、URLリンクされたAppleScriptをデコードした後で、いったんAppleScriptとして構文確認(コンパイル)を実施して、実際のAppleScriptとして評価すべきなのでしょう。
AppleScript名:WordPressの指定IDの記事にリンクされているapplescriptからCocoa Classのproperty宣言を抽出 v2 |
— Created 2018-07-30 by Takaaki Naganoya — Modified 2018-07-31 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "AppKit" use framework "HTMLReader" –https://github.com/nolanw/HTMLReader property |NSURL| : a reference to current application’s |NSURL| property NSString : a reference to current application’s NSString property NSBundle : a reference to current application’s NSBundle property NSPredicate : a reference to current application’s NSPredicate property NSDictionary : a reference to current application’s NSDictionary property NSMutableArray : a reference to current application’s NSMutableArray property NSMutableDictionary : a reference to current application’s NSMutableDictionary property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding –PostID set postID to 3826 set aScheme to "applescript://" –WordPressの指定Post IDの記事を取得してリンクされているURLからURL Schemeでフィルタして、リンクされているAppleScriptのソースを –取得し、AS Sourceからproperty宣言文のみ抽出 set pList to getASSouceLinkedInWordPressPost(postID, aScheme) of me –> {"property NSBundle : a reference to current application’s NSBundle", "property |NSURL| : a reference to current application’s |NSURL|", "property HTMLDocument : a reference to current application’s HTMLDocument", "property NSMutableDictionary : a reference to current application’s NSMutableDictionary", "property NSPredicate : a reference to current application’s NSPredicate", "property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding", "property NSMutableSet : a reference to current application’s NSMutableSet", "property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch", "property NSString : a reference to current application’s NSString", "property NSSortDescriptor : a reference to current application’s NSSortDescriptor"} –property宣言文リストから、propetyがCocoa Classの宣言であるものだけを抽出 set p2List to filterPropertySentenseWhetherCocoaOrNot(pList) of me –> {"NSBundle", "HTMLDocument", "NSMutableDictionary", "NSPredicate", "NSMutableSet", "NSString", "NSSortDescriptor"} on filterPropertySentenseWhetherCocoaOrNot(pList) set outList to {} repeat with i in pList set j to contents of i set j2 to repChar(j, "|", "") of me –Parse String Into Words by Space set aTmpStr to (NSString’s stringWithString:j2) set wList to (aTmpStr’s componentsSeparatedByString:" ") as list if wList contains {"a", "reference", "to", "current", "application’s"} then –通常のクラス名の場合(クラス名以外のpropertyの場合もある) set aTarg to contents of item 2 of wList –property値がCocoa Classかどうかを判定 set fRes to searchClassInFrameworks(aTarg) of me if fRes is not equal to false then set the end of outList to aTarg end if end if end repeat return outList end filterPropertySentenseWhetherCocoaOrNot –指定クラスがいずれかのCocoa Frameworkに所属しているかを検索 on searchClassInFrameworks(aTarget) set aClass to current application’s NSClassFromString(aTarget) if aClass = missing value then return false set theComponenents to (NSBundle’s bundleForClass:aClass)’s bundleURL’s pathComponents() set thePred to NSPredicate’s predicateWithFormat:"pathExtension == ’framework’" set aRes to (theComponenents’s filteredArrayUsingPredicate:thePred)’s firstObject() as list of string or string return aRes end searchClassInFrameworks –指定Post IDのWordPress記事から、指定SchemeのURLを抽出し、AS Sourceをdecodeしてproperty行のみ抽出 on getASSouceLinkedInWordPressPost(postID, aScheme) set {myUser, myPass} to getAcountData() of me tell application "http://piyocast.com/as/xmlrpc.php" set wRes to (call xmlrpc {method name:"wp.getPost", parameters:{"1", myUser, myPass, postID as string}}) end tell set aBody to post_content of wRes –Blog本文 –記事中でリンクしているURLを取得し、指定のURL Schemeでフィルタする set urlList to filterURLLinksByScheme(aBody, aScheme) of me set propList to {} repeat with i in urlList set j to contents of i set urlRec to parseQueryDictFromURLString(j) of me set tmpScript to (urlRec’s |script|) as string –Get AppleScript Source set tList to paragraphs of tmpScript set pList to filterListUsingPredicate(tList, "SELF BEGINSWITH[c] %@", "property") –後方一致 set propList to propList & pList end repeat return propList end getASSouceLinkedInWordPressPost on filterListUsingPredicate(aList as list, aPredicateStr as string, targStr as string) set setKey to current application’s NSMutableSet’s setWithArray:aList set aPredicate to current application’s NSPredicate’s predicateWithFormat_(aPredicateStr, targStr) set aRes to (setKey’s filteredSetUsingPredicate:aPredicate) set bRes to aRes’s allObjects() set cRes to bRes as {list, list of string or string} return cRes end filterListUsingPredicate on parseQueryDictFromURLString(aURLStr as string) if aURLStr = "" then error "No URL String" set aURL to |NSURL|’s URLWithString:aURLStr set aQuery to aURL’s query() –Get Query string part from URL if aQuery’s |length|() = 0 then return false set aDict to NSMutableDictionary’s alloc()’s init() set aParamList to (aQuery’s componentsSeparatedByString:"&") as list repeat with i in aParamList set j to contents of i if length of j > 0 then set tmpStr to (NSString’s stringWithString:j) set eList to (tmpStr’s componentsSeparatedByString:"=") set anElement to (eList’s firstObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding)) set aValStr to (eList’s lastObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding)) (aDict’s setObject:aValStr forKey:anElement) end if end repeat return aDict end parseQueryDictFromURLString –指定のHTML文字列から、Link URLを抽出し、schemeで再抽出する on filterURLLinksByScheme(aBody, aScheme) set conType to "text/html" –HTML文字列をいったんNSDataにしているのは、HTMLReader.frameworkの仕様のため set aData to (current application’s NSString’s stringWithString:aBody)’s dataUsingEncoding:(current application’s NSUTF8StringEncoding) set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType set aTextArray to ((aHTML’s nodesMatchingSelector:"a")’s textContent) as list –リンク文字 set aLinkList to ((aHTML’s nodesMatchingSelector:"a")’s attributes’s valueForKeyPath:"href") as list –URL文字列 set outList to {} repeat with i in aLinkList set j to contents of i if j begins with aScheme then set the end of outList to j end if end repeat return outList end filterURLLinksByScheme –文字置換 on repChar(origText as string, targChar as string, repChar as string) set curDelim to AppleScript’s text item delimiters set AppleScript’s text item delimiters to targChar set tmpList to text items of origText set AppleScript’s text item delimiters to repChar set retText to tmpList as string set AppleScript’s text item delimiters to curDelim return retText end repChar on getAcountData() return {"xxxxxxxx_xx", "XXXXXXXXXXXXXXXXXXXXXXXX"} end getAcountData |
Finderで選択中の画像のうち、最小のものに合わせて各画像の左上を原点にサイズを統一するAppleScriptです。
Finderで選択中のファイルから画像のみ抽出し、そのうちサイズが最小のものに合わせて他の画像をトリミングし、変更したものをデスクトップフォルダに出力します。
▲大きさを揃える画像をFinder上で選択
▲処理前の画像。大きさがまちまち
▲処理後の画像。大きさが最小の画像に合わせてそろっている
スクリーンショット画像を複数撮った場合に、厳密に同じサイズに固定することは(各種スクリーンショット作成ユーティリティを使わないと)行いづらいところです。
そこで、最小の画像を計算で求めて、それに合わせて自動トリミングするようにしてみました。Cocoaの機能を用いて画像処理しているため、Photoshopは必要ありません。
最小の画像を求めるのに、幅+高さの値でソートして最小のものを求めています(widthとheightによる2 key sortではなく1 Keyで済ませたかったので)。
Blogや書籍用の掲載画面図の作成用、といったところでしょうか。Retina画面で撮ったスクリーンショットと非Retina画面のスクリーンショットが混在するとうまく動きません(スクリーンショット画像からはRetina環境で撮った画像であるかどうかを取得できません)。
スクリーンショットの画像からRetina/非Retina環境で撮ったかを判定するのは、(Retina対応機であるかをMachine IDから取得したあとで)Syslogにアクセスしてスクリーンが接続されたか、取り外されたかといった状態を追っていくしかないのかも。
本ScriptをmacOS 10.13.6上で動作確認して驚いたのですが、いままでRecordが返ってくるべき箇所でlistが返ってくる盛大なバグが直って、本ScriptがmacOS 10.13用に書き換えずに動きました(動かないことを確認しようとして動いたので驚いた)。
AppleScript名:Finder上で選択中の画像のうち、最小のものに合わせて各画像の左上を原点にサイズ統一 v2 |
— Created 2018-07-23 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" use framework "QuartzCore" property |NSURL| : a reference to current application’s |NSURL| property NSUUID : a reference to current application’s NSUUID property NSArray : a reference to current application’s NSArray property NSString : a reference to current application’s NSString property NSImage : a reference to current application’s NSImage property NSScreen : a reference to current application’s NSScreen property NSZeroRect : a reference to current application’s NSZeroRect property NSPredicate : a reference to current application’s NSPredicate property NSPNGFileType : a reference to current application’s NSPNGFileType property NSMutableArray : a reference to current application’s NSMutableArray property NSCompositeCopy : a reference to current application’s NSCompositeCopy property NSGraphicsContext : a reference to current application’s NSGraphicsContext property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey property NSCalibratedRGBColorSpace : a reference to current application’s NSCalibratedRGBColorSpace tell application "Finder" set selList to selection as alias list end tell –指定のAlias listのうち画像のみ抽出 set filRes1 to filterAliasListByUTI(selList, "public.image") of me if filRes1 = {} then tell current application –Error Message (No Selection) display dialog "Finder上で選択されているファイルはありません。" buttons {"OK"} default button 1 with icon 1 with title "画像リサイズ処理できません" end tell return end if –各種情報を入れたArrayを作成 set anArray to NSMutableArray’s new() repeat with i in selList set imgPath to (POSIX path of i) set aImage to makeNSImageFromFile(i) of me set sizeInfo to |size|() of aImage set sizeInfo to sizeInfo & {aImg:aImage, total:(width of sizeInfo) + (height of sizeInfo), myPath:imgPath} (anArray’s addObject:sizeInfo) end repeat –最小のサイズの画像の算出 set aRes to anArray’s valueForKeyPath:("@min.total") set bRes to first item of (filterRecListByLabel(anArray, "total == " & aRes as string) of me) –最小サイズの画像 –最小サイズ画像のwidthとheight set minWidth to bRes’s width set minHeight to bRes’s height –環境情報の取得 set aPath to POSIX path of (path to desktop) set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real –> 2.0 (Retina) / 1.0 (Non Retina) set cRes to filterRecListByLabel(anArray, "total != " & aRes as string) of me –最小サイズ以外の画像 repeat with i in cRes set j to contents of i set anImage to aImg of j set fRes to myPath of j set cropedImage to (my cropNSImageBy:{0, 0, minWidth, minHeight} fromImage:anImage) –v1で間違っていた –Retina環境対策 if retinaF > 1.0 then set cropedImage to (my resizedImage:cropedImage toScale:(1.0)) end if –ファイル書き込み set fRes to retUUIDfilePathFromDir(aPath, "png") of me set sRes to saveNSImageAtPathAsPNG(cropedImage, fRes) of me end repeat –リストに入れたレコードを、指定の属性ラベルの値で抽出 on filterRecListByLabel(aRecList as list, aPredicate as string) –ListからNSArrayへの型変換 set aArray to NSArray’s arrayWithArray:aRecList –抽出 set aPredicate to NSPredicate’s predicateWithFormat:aPredicate set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate –NSArrayからListに型変換して返す set bList to filteredArray as list return bList end filterRecListByLabel –Alias listから指定UTIに含まれるものをPOSIX pathのリストで返す on filterAliasListByUTI(aList, targUTI) set newList to {} repeat with i in aList set j to POSIX path of i set tmpUTI to my retUTIfromPath(j) set utiRes to my filterUTIList({tmpUTI}, targUTI) if utiRes is not equal to {} then set the end of newList to j end if end repeat return newList end filterAliasListByUTI –指定のPOSIX pathのファイルのUTIを求める on retUTIfromPath(aPOSIXPath) set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value) if theResult = true then return theValue as string else return theResult end if end retUTIfromPath –UTIリストが指定UTIに含まれているかどうか演算を行う on filterUTIList(aUTIList, aUTIstr) set anArray to NSArray’s arrayWithArray:aUTIList set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr) set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list return bRes end filterUTIList on cropNSImageTo:{x1, y1, x2, y2} fromImage:theImage set newWidth to x2 – x1 set newHeight to y2 – y1 set theSize to (theImage’s |size|()) as record set oldHeight to height of theSize — transpose y value for Cocoa coordintates set y1 to oldHeight – newHeight – y1 set newRect to {{x:x1, y:y1}, {width:newWidth, height:newHeight}} theImage’s lockFocus() set theRep to NSBitmapImageRep’s alloc()’s initWithFocusedViewRect:newRect theImage’s unlockFocus() set outImage to NSImage’s alloc()’s initWithSize:(theRep’s |size|()) outImage’s addRepresentation:theRep return outImage end cropNSImageTo:fromImage: on makeNSImageFromFile(anAlias) set imgPath to (POSIX path of anAlias) set aURL to (|NSURL|’s fileURLWithPath:(imgPath)) return (NSImage’s alloc()’s initWithContentsOfURL:aURL) end makeNSImageFromFile –NSImageを指定の大きさでトリミング on cropNSImageBy:{x1, y1, newWidth, newHeight} fromImage:theImage set theSize to (theImage’s |size|()) as record set oldHeight to height of theSize — transpose y value for Cocoa coordintates set y1 to oldHeight – newHeight – y1 set newRect to {{x:x1, y:y1}, {width:newWidth, height:newHeight}} theImage’s lockFocus() set theRep to NSBitmapImageRep’s alloc()’s initWithFocusedViewRect:newRect theImage’s unlockFocus() set outImage to NSImage’s alloc()’s initWithSize:(theRep’s |size|()) outImage’s addRepresentation:theRep return outImage end cropNSImageBy:fromImage: on retUUIDfilePathFromDir(aPath, aEXT) set aUUIDstr to (NSUUID’s UUID()’s UUIDString()) as string set aPath to ((NSString’s stringWithString:aPath)’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:aEXT return aPath end retUUIDfilePathFromDir –NSImageを指定パスにPNG形式で保存 on saveNSImageAtPathAsPNG(anImage, outPath) set imageRep to anImage’s TIFFRepresentation() set aRawimg to NSBitmapImageRep’s imageRepWithData:imageRep set pathString to NSString’s stringWithString:outPath set newPath to pathString’s stringByExpandingTildeInPath() set myNewImageData to (aRawimg’s representationUsingType:(NSPNGFileType) |properties|:(missing value)) set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean return aRes –true/false end saveNSImageAtPathAsPNG 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 NSBitmapImageRep’s alloc()’s initWithBitmapDataPlanes:(missing value) pixelsWide:aWidth pixelsHigh:aHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:true isPlanar:false colorSpaceName:(NSCalibratedRGBColorSpace) bytesPerRow:0 bitsPerPixel:0 set newSize to {width:aWidth, height:aHeight} aRep’s setSize:newSize NSGraphicsContext’s saveGraphicsState() NSGraphicsContext’s setCurrentContext:(NSGraphicsContext’s graphicsContextWithBitmapImageRep:aRep) aSourceImg’s drawInRect:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) fromRect:(NSZeroRect) operation:(NSCompositeCopy) fraction:(1.0) NSGraphicsContext’s restoreGraphicsState() set newImg to NSImage’s alloc()’s initWithSize:newSize newImg’s addRepresentation:aRep return newImg end resizedImage:toScale: |
note.muの非公開REST API(でも、みんな知っている)を呼び出して、指定ユーザーのノート(投稿記事)を取得するAppleScriptです。
使い慣れたNSURLConnectionがmacOS 10.11で非推奨APIになって久しく、頻繁に使っているREST API呼び出し処理がいきなり動かなくなるのは困るため、NSURLSessionを用いてREST APIを呼び出すよう徐々に変更しています。
当初、REST APIの呼び出し部分は共通ルーチン化して、さまざまなREST APIを共通ルーチンで処理できるとよいだろうかと思っていました。実際にさまざまなREST APIを呼び出してみると、APIごとに意外なほど癖があって、なかなか「すべてのREST APIの共通処理ルーチン」というところまで共通化する段階までには至っていないところ。
左がNSURLSessionを用いた呼び出し、右がNSURLConnectionを用いた呼び出しを行なったものです。せっかくNSURLSessionを使ってみてはいるものの、同一の処理でもNSURLConnectionの方が40%ぐらい高速です。同一ネットワーク環境(iPhone経由のテザリング接続)でNSURLConnectionが0.4秒ぐらい、NSURLSessionでだいたい0.5から0.7秒といったところ。有線ネットワーク接続だともう少し速度が改善するかもしれません。
NSURLSessionに明示的な同期処理が存在しないため、NSURLSessionが受けわたすdelegateイベントをAppleScript側でループしつつ待っており、NSURLConnectionで素直に同期処理メソッドを呼び出すよりもオーバーヘッドが大きくなっているものと推測しています。
NSURLSessionでREST APIを呼び出すあたりの処理をまるごと独立したFrameworkにでも追い出すとよいのではないか、といったところです。
AppleScript名:(GET)指定のユーザーのノート v2 |
— Created 2018-06-16 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" property |NSURL| : a reference to current application’s |NSURL| property NSString : a reference to current application’s NSString property NSURLSession : a reference to current application’s NSURLSession property NSJSONSerialization : a reference to current application’s NSJSONSerialization property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding property NSURLSessionConfiguration : a reference to current application’s NSURLSessionConfiguration property retData : missing value set retData to missing value set reqURLStr to "https://note.mu/api/v1/notes" set aRec to {urlname:"140software"} –note ID set aURL to retURLwithParams(reqURLStr, aRec) of me set aRESTres to callRestGETAPIAndParseResults(aURL) of me on callRestGETAPIAndParseResults(reqURLStr) set aURL to |NSURL|’s URLWithString:reqURLStr set aRequest to NSMutableURLRequest’s requestWithURL:aURL aRequest’s setHTTPMethod:"GET" aRequest’s setTimeoutInterval:10 aRequest’s setValue:"gzip" forHTTPHeaderField:"Content-Encoding" aRequest’s setValue:"application/json; charset=UTF-8" forHTTPHeaderField:"Content-Type" set aConfig to NSURLSessionConfiguration’s defaultSessionConfiguration() set aSession to NSURLSession’s sessionWithConfiguration:aConfig delegate:(me) delegateQueue:(missing value) set aTask to aSession’s dataTaskWithRequest:aRequest aTask’s resume() –Start URL Session repeat 10000 times if retData is not equal to missing value then exit repeat current application’s NSThread’s sleepForTimeInterval:("0.001" as real) –delay 0.001 end repeat retData end callRestGETAPIAndParseResults on URLSession:tmpSession dataTask:tmpTask didReceiveData:tmpData set aStat to (tmpTask’s state()) as list of string or string set resStr to NSString’s alloc()’s initWithData:tmpData encoding:(NSUTF8StringEncoding) set jsonString to NSString’s stringWithString:(resStr) set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding) set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value) set retData to aJsonDict as list of string or string end URLSession:dataTask:didReceiveData: on retURLwithParams(aBaseURL, aRec) set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec set aKeyList to (aDic’s allKeys()) as list set aValList to (aDic’s allValues()) as list set aLen to length of aKeyList set qList to {} repeat with i from 1 to aLen set aName to contents of item i of aKeyList set aVal to contents of item i of aValList set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal) end repeat set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL aComp’s setQueryItems:qList set aURL to (aComp’s |URL|()’s absoluteString()) as text return aURL end retURLwithParams |
指定画像を任意の色で単色化し、指定色名をファイル名に反映させてファイル出力するAppleScriptです。
もとは、「色付き単色画像を作成する」AppleScriptに、
・choose colorによるユーザー選択色を反映
・choose colorで指定した色のざっくりとした色名検出
などの機能をつなぎ合わせたものです。
日本地図の白地図からカラーバリエーション画像を出力する際に作成しました。線が黒で書かれている白地図(白地図というのはそういうものですが)から、さまざまな色のバリエーションを作成。その際に、どのような色を指定したかをファイル名に反映させておいたほうが便利だったので、そのようにしてみました。
GPUImage.frameworkを利用しています。
–> GPUImage.framework (To ~/Library/Frameworks/)
色名推定のロジックもけっこうしっくりきていますが、明度も反映して、明るいものは「light-」、暗いものは「dark-」と出力させてもいいような気がします。ただし、「dark-orange」はBrownのことですし、ほかにも色名を別途振り直したほうがいいパターンもあるように思われます。
AppleScript名:色付き単色画像を作成する(自由色指定&色名推定) |
— Created 2017-02-11 by Takaaki Naganoya — Modified 2018-07-15 by Takaaki Naganoya — 2017-2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "AppKit" use framework "GPUImage" –https://github.com/BradLarson/GPUImage property NSUUID : a reference to current application’s NSUUID property NSColor : a reference to current application’s NSColor property NSString : a reference to current application’s NSString property NSImage : a reference to current application’s NSImage property NSBezierPath : a reference to current application’s NSBezierPath property NSPNGFileType : a reference to current application’s NSPNGFileType property GPUImagePicture : a reference to current application’s GPUImagePicture property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep –Select Image File set aFile to POSIX path of (choose file of type {"public.image"} with prompt "Select image") –Select Color set {rCol, gCol, bCol} to choose color set fillColorR to makeNSColorFromRGBAval(rCol, gCol, bCol, 65535, 65535) of me set fillColorRName to retColorDomainNameFromNSColor(fillColorR) of me –だいたいの色名称を計算 set imgRes to makeMonoColoredImage(aFile, fillColorR) of me set fRes to retUUIDandKeyfilePath(aFile, fillColorRName, "png") of me set bRes to saveNSImageAtPathAsPNG(imgRes, fRes) of me — return "UUID_aKey.aEXT" full path on retUUIDandKeyfilePath(aPath, aKey, aEXT) set aUUIDstr to ((NSUUID’s UUID()’s UUIDString()) as string) & "_" & aKey set aPath to ((NSString’s stringWithString:aPath)’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:aEXT return aPath end retUUIDandKeyfilePath on makeMonoColoredImage(aFile, NScolorObj) set aImage to NSImage’s alloc()’s initWithContentsOfFile:aFile return makeColoredNSImageWithColor(aImage, NScolorObj) of me –色付き単色画像を作成する end makeMonoColoredImage on makeColoredNSImageWithColor(aImage, fillColor) set aSize to aImage’s |size|() set aWidth to width of aSize set aHeight to height of aSize set newNSImage to makeNSImageWithFilledWithColor(aWidth, aHeight, fillColor) set grayImage to filterWithNSImage(aImage, "GPUImageGrayscaleFilter") of me set compImage to composeImageWithBlendFilter(grayImage, newNSImage, "GPUImageScreenBlendFilter") of me return compImage end makeColoredNSImageWithColor 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 on composeImageWithBlendFilter(aImage, bImage, filterName) set aClass to current application’s NSClassFromString(filterName) set blendFilter to aClass’s alloc()’s init() set pictureA to GPUImagePicture’s alloc()’s initWithImage:aImage pictureA’s addTarget:blendFilter pictureA’s processImage() set imgRes to blendFilter’s imageByFilteringImage:bImage return imgRes end composeImageWithBlendFilter –指定サイズの画像を作成し、指定色で塗ってNSImageで返す on makeNSImageWithFilledWithColor(aWidth, aHeight, fillColor) –Imageの作成 set curSize to current application’s NSMakeSize(aWidth, aHeight) set anImage to NSImage’s alloc()’s initWithSize:curSize anImage’s lockFocus() –描画開始 set theRect to {{x:0, y:0}, {height:aHeight, width:aWidth}} set theNSBezierPath to NSBezierPath’s bezierPath theNSBezierPath’s appendBezierPathWithRect:theRect fillColor’s |set|() –色設定 theNSBezierPath’s fill() –ぬりつぶし anImage’s unlockFocus() –描画ここまで –生成した画像のRaw画像を作成 set imageRep to anImage’s TIFFRepresentation() set aRawimg to NSBitmapImageRep’s imageRepWithData:imageRep set newImg to NSImage’s alloc()’s initWithSize:curSize newImg’s addRepresentation:aRawimg return newImg end makeNSImageWithFilledWithColor –NSImageを指定パスにPNG形式で保存 on saveNSImageAtPathAsPNG(anImage, outPath) set imageRep to anImage’s TIFFRepresentation() set aRawimg to NSBitmapImageRep’s imageRepWithData:imageRep set pathString to NSString’s stringWithString:outPath set newPath to pathString’s stringByExpandingTildeInPath() set myNewImageData to (aRawimg’s representationUsingType:(NSPNGFileType) |properties|:(missing value)) set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean return aRes –true/false end saveNSImageAtPathAsPNG –NSColor(内容はRGBAカラー)からだいたいの色名を推測 on retColorDomainNameFromNSColor(aCol) set hueVal to aCol’s hueComponent() set satVal to aCol’s saturationComponent() set brightVal to aCol’s brightnessComponent() if satVal ≤ 0.01 then set satVal to 0.0 set colName to "" if satVal = 0.0 then if brightVal ≤ 0.2 then set colName to "black" else if (brightVal > 0.95) then set colName to "white" else set colName to "gray" end if else if hueVal ≤ (15.0 / 360) or hueVal ≥ (330 / 360) then set colName to "red" else if hueVal ≤ (45.0 / 360) then set colName to "orange" else if hueVal < (70.0 / 360) then set colName to "yellow" else if hueVal < (150.0 / 360) then set colName to "green" else if hueVal < (190.0 / 360) then set colName to "green(cyan)" else if (hueVal < 250.0 / 360.0) then set colName to "blue" else if (hueVal < 290.0 / 360.0) then set colName to "purple" else set colName to "red(magenta)" end if end if return colName end retColorDomainNameFromNSColor on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer) set aRedCocoa to (redValue / aMaxVal) as real set aGreenCocoa to (greenValue / aMaxVal) as real set aBlueCocoa to (blueValue / aMaxVal) as real set aAlphaCocoa to (alphaValue / aMaxVal) as real set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa return aColor end makeNSColorFromRGBAval |
日本測地系の緯度・経度情報から世界測地系の緯度・経度情報に変換するAppleScriptです。
AppleScriptに35種類の関数計算機能を付加する「calcLibAS」の計算精度を確認するための実例でもあります。だいたい期待の精度は出せている感じです。
より精度を求められるような用途には、AppleScriptの数値変数自体が浮動小数点演算で10桁の計算精度しかないので、数値計算自体を外部にすべて出してしまうか、より精度の高い変数型を作ってしまうか(文字列で扱うようにすれば問題なさそう)というところでしょうか。
「日本測地系から世界測地系への座標変換(近似式)」のほうはObjective-Cから、「日本測地系から世界測地系への直行座標変換」のほうはRubyからAppleScriptに書き換えてみたものです。
Rubyのプログラムが割と単調だったので、読んでいて疲れてしまいました。変数名の付け方が途中で変わっているのは、「とりあえず動けばいい」ぐらいで組んでいたのと、変数名にいちいち「Local」とか付けるのに疲れたからです。
Piyomaru Script Assistantで変数名のみ一括置換してもいいわけですが、それはそれ、、、
AppleScript名:日本測地系から世界測地系への座標変換(近似式) |
— Created 2018-07-13 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" set jpLat to 42.16593046887 set jpLng to 142.771877437246 set {wdLat, wdLng, wdH} to tky2wgsLinea(jpLat, jpLng, 0) of me –日本測地系から世界測地系に換算 –> {42.168515890674, 142.768119997121, 0} set {latJ, lonJ, heightJ} to wgs2tkyLinea(wdLat, wdLng, wdH) of me –世界測地系から日本測地系に換算 –> {42.16593052955, 142.77187743437, 0} –日本測地系から世界測地系に換算 –http://d.hatena.ne.jp/iRSS/20111112/1321113232 on tky2wgsLinea(latJ, lonJ, heightJ) set heightW to heightJ set lonW to lonJ – latJ * 4.6038E-5 – lonJ * 8.3043E-5 + 0.01004 set latW to latJ – latJ * 1.0695E-4 + lonJ * 1.7464E-5 + 0.0046017 return {latW, lonW, heightW} end tky2wgsLinea –世界測地系から日本測地系に換算 –https://altarf.net/computer/技術的なポエム/3332 on wgs2tkyLinea(latW, lonW, heightW) set heightJ to heightW set latJ to (latW * 1.000106961) – (lonW * 1.7467E-5) – 0.004602017 set lonJ to (lonW * 1.000083049) + (latW * 4.6047E-5) – 0.010041046 return {latJ, lonJ, heightJ} end wgs2tkyLinea |
AppleScript名:日本測地系から世界測地系への直行座標変換 |
— Created 2018-07-13 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use math : script "calcLibAS" –http://piyocast.com/as/archives/3523 set jpLat to 42.16593046887 set jpLng to 142.771877437246 set {wdLat, wdLng, wdH} to JapanGeodeticSystem’s convertIntoWgs(jpLat, jpLng) –> {42.168504277889, 142.768075650137, 61.914452206343} –https://altarf.net/computer/ruby/3347 –日本測地系から世界測地系に換算 script JapanGeodeticSystem –ラジアン(度) property rdNum : pi / 180 –日本測地系の定数(ベッセル楕円体) property rJP : 6.377397155E+6 –赤道半径 property fJP : 1 / 299.1528128 –扁平率 property e2JP : (2 * fJP) – (fJP * fJP) –第一離心率 –世界測地系の定数(WGS84) property rWS : 6.378137E+6 –赤道半径 property fWS : 1 / 298.257223563 –扁平率 property e2WS : (2 * fWS) – (fWS * fWS) –第一離心率 –並行移動量(m) property dxNum : -148 property dyNum : 507.0 property dzNum : 681.0 –楕円体高 property heightNum : 0 –日本測地系から世界測地系に変換する on convertIntoWgs(aLat, aLong) set {x, y, z} to llh2xyz(aLat, aLong, heightNum, rJP, e2JP, rdNum) of me set x to x + dxNum set y to y + dyNum set z to z + dzNum set {lat1, lng1, h1} to xyz2llh(x, y, z, rWS, e2WS, rdNum) of me return {lat1, lng1, h1} end convertIntoWgs –座標系の変換(緯度経度 -> xyz) on llh2xyz(latLocal, lngLocal, hLocal, aLocal, e2Local, rdLocal) set latLocal to latLocal * rdLocal set lngLocal to lngLocal * rdLocal set sbNum to calcSin(latLocal) of math set cbNum to calcCos(latLocal) of math set rnLocal to aLocal / (calcSqrt(1 – e2Local * sbNum * sbNum) of math) set xLocal to (rnLocal + hLocal) * cbNum * (calcCos(lngLocal) of math) set yLocal to (rnLocal + hLocal) * cbNum * (calcSin(lngLocal) of math) set zLocal to (rnLocal * (1 – e2Local) + hLocal) * sbNum return {xLocal, yLocal, zLocal} end llh2xyz –座標系の変換(xyz -> 緯度経度) on xyz2llh(x, y, z, a, e2, rdLocal) set bda to calcSqrt(1 – e2) of math set p to calcSqrt(x * x + y * y) of math set t to calcAtan2(z, p * bda) of math set stNum to calcSin(t) of math set ctNum to calcCos(t) of math set b to calcAtan2(z + e2 * a / bda * stNum * stNum * stNum, p – e2 * a * ctNum * ctNum * ctNum) of math set l to calcAtan2(y, x) of math set sb to calcSin(b) of math set rn to a / (calcSqrt(1 – e2 * sb * sb) of math) set h to p / (calcCos(b) of math) – rn set l1 to b / rdLocal set l2 to l / rdLocal return {l1, l2, h} end xyz2llh end script |
オープン中のNumbers書類のうち最前面のものを対象に、すべてのシート上のテーブル1のデータを連結して新規書類に入れるAppleScriptです。
(↑)Numbers書類中のテーブルのカラム数がすべて同じことが本Scriptの前提条件。
データ結合まではすぐに処理が終わるものの、結合したデータを新規Numbers書類にAppleScriptから順次突っ込むと、データ数の増加にともなって処理時間が大幅に増えるので、そのやり方はやるべきではありません(経験のない素人がやりそうな処理)。
なので、本ScriptではCSVファイルを組み立てて、デスクトップに書き出し、それをNumbersにオープンさせます。
これだと処理は一瞬です。開発環境では、シート9枚&総合計263行のデータを連結してCSVに書き出してNumbers上でオープンするのに1.6秒しかかかりません。
AppleScript名:オープン中のNumbers書類のすべてのシート上のテーブルのデータを連結して新規書類に入れる v2 |
— Created 2018-07-09 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.5" use scripting additions use framework "Foundation" use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html script spd property allData : {} –Numbersから取得したデータをストアしておく property all2DData : {} –取得したデータを2D Arrayに変換した際にデータをストアしておく end script set (allData of spd) to {} set (all2DData of spd) to {} set dataWidth to 2 tell application "Numbers" tell front document set aList to every sheet repeat with i in aList tell i tell table 1 –テーブル中のセルの値をすべて取得(1D Listで帰ってくる) set aList to value of every cell –1D ListをSweep(missing valueの入っているアイテムを削除) set bList to sweep1DList(aList) of me –取得してSweepしたデータを連結 set (allData of spd) to (allData of spd) & (bList as list) end tell end tell end repeat end tell end tell –1D Listを2D Listに変換 set (all2DData of spd) to conv1DListTo2DList((allData of spd), dataWidth) of me –まとめたデータをCSVファイルとして書き出し set aPath to ((path to desktop) as string) & (do shell script "uuidgen") & ".csv" saveAsCSV((all2DData of spd), aPath) of me do shell script "sync" –SSDで処理している場合には必要ないが、、、 set anAlias to aPath as alias –書き出したCSVファイルをNumbersでオープン tell application "Numbers" open anAlias activate end tell on sweep1DList(aList) load framework return (current application’s SMSForder’s arrayByDeletingBlanksIn:aList) as list end sweep1DList on conv1DListTo2DList(aList, groupingNum) load framework return (current application’s SMSForder’s subarraysFrom:aList groupedBy:groupingNum |error|:(missing value)) as list end conv1DListTo2DList –2D List to CSV file on saveAsCSV(aList, aPath) set crlfChar to (string id 13) & (string id 10) set LF to (string id 10) set wholeText to "" repeat with i in aList set newLine to {} –Sanitize (Double Quote) repeat with ii in i set jj to ii as text set kk to repChar(jj, string id 34, (string id 34) & (string id 34)) of me –Escape Double Quote set the end of newLine to kk end repeat –Change Delimiter set aLineText to "" set curDelim to AppleScript’s text item delimiters set AppleScript’s text item delimiters to "\",\"" set aLineList to newLine as text set AppleScript’s text item delimiters to curDelim set aLineText to repChar(aLineList, return, "") of me –delete return set aLineText to repChar(aLineText, LF, "") of me –delete lf set wholeText to wholeText & "\"" & aLineText & "\"" & crlfChar –line terminator: CR+LF end repeat if (aPath as string) does not end with ".csv" then set bPath to aPath & ".csv" as Unicode text else set bPath to aPath as Unicode text end if writeToFile(wholeText, bPath, false) of me end saveAsCSV on writeToFile(this_data, target_file, append_data) tell current application try set the target_file to the target_file as text set the open_target_file to open for access file target_file with write permission if append_data is false then set eof of the open_target_file to 0 write this_data to the open_target_file starting at eof close access the open_target_file return true on error error_message try close access file target_file end try return error_message end try end tell end writeToFile on repChar(origText as text, targChar as text, repChar as text) set curDelim to AppleScript’s text item delimiters set AppleScript’s text item delimiters to targChar set tmpList to text items of origText set AppleScript’s text item delimiters to repChar set retText to tmpList as string set AppleScript’s text item delimiters to curDelim return retText end repChar |