画像ファイルを読み込んで、クリップボードに設定する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の機能を呼び出す方法は、ここまでやってダメならあきらめがつくというところでしょうか。
指定の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: |
本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 |
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 |
PDFから指定ページ以降を削除するAppleScriptです。
こんな(↑)418ページもあるPDFから、おためし版の、冒頭から40ページだけ抽出したPDFを作成する際に使用したものです。
もともとは418ページありましたが、、、
実行後は40ページに、、、
AppleScript名:PDFから指定ページ以降を削除 |
— Modified 2018-07-08 by Takaaki Naganoya –Original By Shane Stanley use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "AppKit" use framework "QuartzCore" use aBplus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html property NSSet : a reference to current application’s NSSet property |NSURL| : a reference to current application’s |NSURL| property NSArray : a reference to current application’s NSArray property SMSForder : a reference to current application’s SMSForder property NSIndexSet : a reference to current application’s NSIndexSet property PDFDocument : a reference to current application’s PDFDocument property NSSortDescriptor : a reference to current application’s NSSortDescriptor load framework set sPage to 41 –ここから末尾までのページを削除 set inFile to (choose file of type {"pdf"} with prompt "Choose your PDF files:") set pRes to removePDFPageAfter(inFile, sPage) of me –指定パスのPDFの指定ページ以降をすべて削除 on removePDFPageAfter(inFile, sPage) set pCount to pdfPageCount(inFile) of me set eCount to pCount – sPage + 1 set aindexSet to NSIndexSet’s indexSetWithIndexesInRange:(current application’s NSMakeRange(sPage, eCount)) set targPageList to (SMSForder’s arrayWithIndexSet:aindexSet) as list return removeSpecificPagesFromPDF(inFile, targPageList) of me end removePDFPageAfter –指定PDF書類の複数ページの一括削除 on removeSpecificPagesFromPDF(inFileAlias, targPageNumList as list) set inNSURL to |NSURL|’s fileURLWithPath:(POSIX path of inFileAlias) set theDoc to PDFDocument’s alloc()’s initWithURL:inNSURL –削除対象ページリストをユニーク化して降順ソート(後方から削除) set pRes to theDoc’s pageCount() set t3List to relativeToAbsNumList(targPageNumList, pRes) of me repeat with i in t3List copy i to targPageNum (theDoc’s removePageAtIndex:(targPageNum – 1)) end repeat –Overwrite Exsiting PDF set aRes to (theDoc’s writeToURL:inNSURL) as boolean return aRes end removeSpecificPagesFromPDF –絶対ページと相対ページが混在した削除対象ページリストを絶対ページに変換して重複削除して降順ソート on relativeToAbsNumList(aList, aMax) set newList to {} repeat with i in aList set j to contents of i if i < 0 then set j to aMax + j end if if (j ≤ aMax) and (j is not equal to 0) then set the end of newList to j end if end repeat set t1List to uniquify1DList(newList, true) of me set t2List to sort1DNumListWithOrder(t1List, false) of me return t2List end relativeToAbsNumList –1D/2D Listをユニーク化 on uniquify1DList(theList as list, aBool as boolean) set aArray to NSArray’s arrayWithArray:theList set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self" return bArray as list end uniquify1DList –Sort 1-Dimension List(String Number List) on sort1DNumListWithOrder(theList as list, aBool as boolean) tell NSSet to set theSet to setWithArray_(theList) tell NSSortDescriptor to set theDescriptor to sortDescriptorWithKey_ascending_("floatValue", aBool) set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor} return (sortedList) as list end sort1DNumListWithOrder –指定PDFのページ数をかぞえる(10.9対応。普通にPDFpageから取得) –返り値:PDFファイルのページ数(整数値) on pdfPageCount(aFile) set aFile to POSIX path of aFile set theURL to |NSURL|’s fileURLWithPath:aFile set aPDFdoc to PDFDocument’s alloc()’s initWithURL:theURL set aRes to aPDFdoc’s pageCount() return aRes as integer end pdfPageCount |
CotEditorの最前面のテキスト中で、空行で区切ったブロックを逆順にならべかえるAppleScriptです。
こういう(↑)状態のテキストを、
こういう(↑)状態にならべかえます。
ここでは、わかりやすく空行でブロックを区切っていますが、別のもので区切りを検出してみたり、テキスト全体から自動的にブロック検出をテキストエディタ自体がやってくれるといいのになー、と思っています。
AppleScript名:CotEditorの最前面のテキストで空行で区切ったブロックを逆順にする |
— Created 2018-07-07 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" –CotEditorから本文テキストを取得 tell application "CotEditor" tell front document set aText to contents of it end tell set aList to paragraphs of aText end tell –空行で区切られたテキストブロックをブロック単位で逆順に組み立てる set bText to makeRetrunSeparatedStrBlockReversed(aList) of me –計算結果をCotEditorの本文テキストに書き戻す tell application "CotEditor" tell front document set (contents of it) to bText end tell end tell on makeRetrunSeparatedStrBlockReversed(aList) –行ごとにリスト化した1D Listをブロック単位の2D List化 set newList to {} set tmpList to {} repeat with i in aList set j to contents of i if j = "" then if tmpList = {} then set tmpList to {} else set the end of newList to tmpList set the end of newList to {} set tmpList to {} end if else set the end of tmpList to j end if end repeat if tmpList is not equal to {} then set the end of newList to tmpList set the end of newList to {} end if set newBList to reverse of newList –ブロックを逆順に –2D Listをテキストに set newCList to FlattenList(newBList) of me set resBstr to retStrFromArrayWithDelimiter(newCList, return) of me return resBstr end makeRetrunSeparatedStrBlockReversed –By Paul Berkowitz –2009年1月27日 2:24:08:JST –Re: Flattening Nested Lists on FlattenList(aList) set oldDelims to AppleScript’s text item delimiters set AppleScript’s text item delimiters to {"????"} set aString to aList as text set aList to text items of aString set AppleScript’s text item delimiters to oldDelims return aList end FlattenList –リストを指定デリミタをはさんでテキスト化 on retStrFromArrayWithDelimiter(aList, aDelim) set anArray to current application’s NSArray’s arrayWithArray:aList set aRes to anArray’s componentsJoinedByString:aDelim return aRes as text end retStrFromArrayWithDelimiter |
Finder上で選択中のMarkdown書類を取得し、ファイル名で昇順ソート(A→Z)して、各Markdown書類中のタイトルのうち最もレベルの高い(重要な)ものを抽出し、まとめてテキストにしてクリップボードに転送するAppleScriptです。
大量にあるMarkdown書類の本文中から一番重要なタイトルを抽出する作業を……さすがに手作業で行うわけには行かなかったので、AppleScriptを書いて処理してみました。ただし、新規に書いた処理はほとんどなく、既存のルーチンの寄せ合わせで構成しています。
Finder上でMarkdown書類を選択した状態で本Scriptを実行すると(Markdown書類以外は除外して)、AppleScriptでMarkdown書類を読み込んで(Markdown書類がUTF-8固定なのでUTF-8を指定して読み込み)、正規表現で書類中のタイトルを抽出し、重要なタイトル(#の個数が少ない)をピックアップ。これをすべての書類について行います。
処理結果をクリップボードに転送します。
最終的には、(手作業で加工して)このようになります。
若干の手作業は発生してしまいますが、このScriptを組まなかったら、とてもその手作業を行う気も起こらなかったわけで、、、
AppleScript名:Finder上で選択中のMarkdown書類をファイル名で昇順ソート(A→Z)して、各書類のタイトル見出しを取得する |
— Created 2018-06-26 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "Quartz" use mdLib : script "Metadata Lib" version "1.0.0" use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html 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 SMSForder : a reference to current application’s SMSForder property NSIndexSet : a reference to current application’s NSIndexSet property NSPredicate : a reference to current application’s NSPredicate property NSMutableSet : a reference to current application’s NSMutableSet property NSFileManager : a reference to current application’s NSFileManager property NSCountedSet : a reference to current application’s NSCountedSet property NSURLPathKey : a reference to current application’s NSURLPathKey property NSMutableArray : a reference to current application’s NSMutableArray property NSURLNameKey : a reference to current application’s NSURLNameKey property NSSortDescriptor : a reference to current application’s NSSortDescriptor property NSURLIsPackageKey : a reference to current application’s NSURLIsPackageKey property NSRegularExpression : a reference to current application’s NSRegularExpression property NSURLIsDirectoryKey : a reference to current application’s NSURLIsDirectoryKey property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey property NSURLContentModificationDateKey : a reference to current application’s NSURLContentModificationDateKey property NSRegularExpressionAnchorsMatchLines : a reference to current application’s NSRegularExpressionAnchorsMatchLines property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles property NSRegularExpressionDotMatchesLineSeparators : a reference to current application’s NSRegularExpressionDotMatchesLineSeparators load framework –set inFiles to (choose file of type {"pdf"} with prompt "Choose your PDF files:" with multiple selections allowed) tell application "Finder" set inFiles to selection as alias list end tell if inFiles = {} then return –指定のAlias listのうちMarkdown書類のみ抽出 set filRes1 to filterAliasListByUTI(inFiles, "net.daringfireball.markdown") of me –指定のPOSIX path listから、各Markdown書類中の一番重要な見出しを抽出して返す set tRes to listTitlesFromMarkdownDocPathList(filRes1) of me –取得したタイトル一覧リストをテキストに変換 set t2Res to retStrFromArrayWithDelimiter(tRes, return) of me –クリップボードに結果を転送 set the clipboard to t2Res on listTitlesFromMarkdownDocPathList(inFiles) set outList to {} set inFilesSorted to my filesInListSortAscending(inFiles) repeat with i in inFilesSorted –POSIX pathからaliasにパス変換してテキスト読み込み set j to POSIX file (contents of i) set jj to j as alias set aStr to (read jj as «class utf8») set aList to retHeaders(aStr) of me –Markdown書類中の見出しをリストアップ –> {{1, " 2008/3/9 5桁の乱数を生成"}} if aList is not equal to {} then –2D Listの昇順ソート set sortIndexes to {0} –Key Item id: begin from 0 set sortOrders to {true} –ascending = true set sortTypes to {"compare:"} set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list set aCon to contents of second item of first item of resList set the end of outList to aCon end if end repeat return outList end listTitlesFromMarkdownDocPathList on filesInListSortAscending(aliasList as list) set cList to {} repeat with i in aliasList set j to contents of i set aFileName to ((current application’s NSString’s stringWithString:j)’s valueForKeyPath:"lastPathComponent") set the end of cList to {fileName:aFileName, pathDat:j} end repeat set aResList to sortRecListByLabel(cList, "fileName", true) of me –昇順ソート set bResList to (aResList’s valueForKeyPath:"pathDat") as list of string or string return bResList end filesInListSortAscending –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 returnNumberCharsOnly(aStr) set anNSString to current application’s NSString’s stringWithString:aStr set anNSString to anNSString’s stringByReplacingOccurrencesOfString:"[^0-9]" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()} return anNSString as text end returnNumberCharsOnly –リストに入れたレコードを、指定の属性ラベルの値でソート on sortRecListByLabel(aRecList as list, aLabelStr as string, ascendF as boolean) set aArray to current application’s NSArray’s arrayWithArray:aRecList set sortDesc to current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF set sortDescArray to current application’s NSArray’s arrayWithObjects:sortDesc set sortedArray to aArray’s sortedArrayUsingDescriptors:sortDescArray return sortedArray end sortRecListByLabel –見出し抽出用サブルーチン群 on retHeaders(aCon) set tList to {} set regStr to "^#{1,6}[^#]*?$" set headerList to my findPattern:regStr inString:aCon repeat with i in headerList set j to contents of i set regStr2 to "^#{1,6}[^#]*?" set headerLevel to length of first item of (my findPattern:regStr2 inString:j) set the end of tList to {headerLevel, text (headerLevel + 1) thru -1 in j} end repeat return tList end retHeaders on findPattern:thePattern inString:theString set theOptions to ((NSRegularExpressionDotMatchesLineSeparators) as integer) + ((NSRegularExpressionAnchorsMatchLines) as integer) set theRegEx to NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value) set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString} set theFinds to theFinds as list — so we can loop through set theResult to {} — we will add to this set theNSString to NSString’s stringWithString:theString repeat with i from 1 to count of items of theFinds set theRange to (item i of theFinds)’s range() set end of theResult to (theNSString’s substringWithRange:theRange) as string end repeat return theResult end findPattern:inString: –リストを指定デリミタをはさんでテキスト化 on retStrFromArrayWithDelimiter(aList, aDelim) set anArray to current application’s NSArray’s arrayWithArray:aList set aRes to anArray’s componentsJoinedByString:aDelim return aRes as list of string or string end retStrFromArrayWithDelimiter |
CotEditorでオープン中の最前面の書類中のテキストの、日付フォーマットを変換するAppleScriptです。
▲スクリーンショットは作成中のものです
Blogのアーカイブ本のオマケとして(記事だけだとウンザリする仕上がりだったので)、冒頭にその年に何があったかをAppleのニュースリリースから抽出して掲載してみたのですが、日付の形式が独特だったので、手で打ち直していました。
繰り返して行うと面倒だったので、NSDataDetectorで自然言語テキスト(CotEditorの本文テキスト)から日付を自動検出して書き換えを試みたのですが、NSDataDetectorではこのサンプルのようなほんのちょっとだけ手の加わった日付テキストでは検出してくれませんでした。
一応、フォーマット自体は固定だったので、フォーマッターだけ指定すれば日付テキストとして認識し、常識的なYYYY/MM/DDの日付フォーマットに書き換えるようにしてみました。
▲日付フォーマット文字列の入力
▲日付フォーマット置換したテキスト(処理は一瞬)
ただし、日付フォーマットテキストが行で独立しているもの(添付スクリーンショットのように)を処理対象にしているので、あまり汎用性はなさそうです。
一般的には正規表現で指定することになるんでしょうけれど、その正規表現というかもともとの日付フォーマットを元のテキストから自動検出してくれることがベストだと思います。
なお、CotEditorに依存した処理はひとつもないので、JeditΩでもmiでもテキストエディットでもTextWranglerでもBBEditでも、対象にして処理することはかんたんです。クリップボードの内容に対して処理したほうがいいのかもしれません。
AppleScript名:CotEditorの最前面のテキストの日付フォーマットを変換する |
— Created 2018-07-07 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.5" use framework "Foundation" use scripting additions property NSArray : a reference to current application’s NSArray property NSString : a reference to current application’s NSString property NSLocale : a reference to current application’s NSLocale property NSDictionary : a reference to current application’s NSDictionary property NSCountedSet : a reference to current application’s NSCountedSet property NSMutableArray : a reference to current application’s NSMutableArray property NSSortDescriptor : a reference to current application’s NSSortDescriptor property NSDateFormatter : a reference to current application’s NSDateFormatter set tmpArray to NSMutableArray’s new() –変換元(テキストから自動取得できるとよかったのに) set dFfromStr to text returned of (display dialog "Input Date Format:(年=yyyy, 月=MM, 日=dd)" default answer "MM月 dd, yyyy") –変換先 set dOutFormStr to "yyyy/MM/dd" tell application "CotEditor" tell front document set aRes to contents of it end tell set aList to paragraphs of aRes end tell repeat with i in aList set j to contents of i set theDate to dateFromStringWithDateFormat(j, dFfromStr) of me if theDate is not equal to missing value then set newDateStr to convDateObjToStrWithFormat(theDate, dOutFormStr) of me (tmpArray’s addObject:newDateStr) else (tmpArray’s addObject:j) end if end repeat –NSArrayを指定デリミタをはさんでテキスト化 set tRes to tmpArray’s componentsJoinedByString:(return) set ttRes to tRes as string tell application "CotEditor" tell front document set (contents of it) to ttRes end tell end tell –日付文字列からdate objectを作成する on dateFromStringWithDateFormat(dateString as string, dateFormat as string) 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 list of string or string end dateFromStringWithDateFormat –date objectから指定の日付文字列を作成する on convDateObjToStrWithFormat(aDateO as date, aFormatStr as string) set aDF to NSDateFormatter’s alloc()’s init() set aLoc to NSLocale’s currentLocale() set aLocStr to (aLoc’s localeIdentifier()) as string aDF’s setLocale:(NSLocale’s alloc()’s initWithLocaleIdentifier:aLocStr) aDF’s setDateFormat:aFormatStr set dRes to (aDF’s stringFromDate:aDateO) as string return dRes end convDateObjToStrWithFormat |
指定の単語リストから登場頻度の計算(ヒストグラム)を行い、指定単語リストに入っているものについては除外するAppleScriptです。
指定のPDFからURLリンクを抽出し、そのリンクURLをデコードしてAppleScriptのURLリンクからソーステキストを取得し、単語ごとにparseして集計してタグクラウドのRTFを出力するAppleScriptを作成したときに使用したものです。
単に、プログラムリストをparseしてタグクラウドを作成しただけでは、あまり本質(ノイズ要素を除外した分析したい対象のデータ)が見えてきませんでした。
変数名やコマンドなどを除外したかったので、これら(分析対象外のデータ=ノイズ)を集計時に除外するという処理を加えてみたものです。
AppleScript名:NSCountedSetでヒストグラム計算、指定単語リストを削除 |
— Created 2018-07-06 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" set theList to {"set", "set", "set", "tell", "application", "application", "Finder", "Finder", "Safari"} set exList to {"Finder", "Safari"} set aRes to wordsCountWithRemovingWordList(theList, exList) of me –> {{theKey:"set", theCount:3}, {theKey:"application", theCount:2}, {theKey:"tell", theCount:1}} on wordsCountWithRemovingWordList(theList as list, excludeWordList as list) set theCountedSet to current application’s NSCountedSet’s alloc()’s initWithArray:theList set newArray to current application’s NSMutableArray’s new() set objList to (theCountedSet’s allObjects()) as list repeat with i in objList set j to contents of i if j is not in excludeWordList then (newArray’s addObject:{theKey:j, theCount:(theCountedSet’s countForObject:j)}) end if end repeat return newArray as list of string or string end wordsCountWithRemovingWordList |
「システム環境設定」の「サウンド」で、「ユーザーインタフェースのサウンドエフェクトを再生」がオンになっているかどうかを確認するAppleScriptです。
▲false(左)、true(右)
本Blogのアーカイブ本の作成のために過去の記事を整理していたら、本Scriptを発掘し、いまの環境では動かない記述内容だったので書き換えてみたものです。
▲アーカイブ本、1年分で300〜400ページぐらいになりそうで、、、
AppleScript名:System UI Soundが有効になっているかどうかをチェック v2 |
set sRes to getEnableUISound() of me –> true / false –System UI Soundが有効になっているかどうかをチェック on getEnableUISound() set a to do shell script "defaults read ’Apple Global Domain’ ’com.apple.sound.uiaudio.enabled’" return (a = "1") end getEnableUISound |