Archive for 9月, 2015

2015/09/30 Unicodeの文字をNormalizeする

Cocoaの機能を用いて、NSStringの文字列をNormalizeするAppleScriptです。

たまたま仕事で、PDFの内容(テキスト)をparseしてデータを取り出して文字コードの範囲チェックをしていたところ・・・特定の文字のコード判定が思ったようにできず・・・調べてみたら、濁点つきのひらがな&カタカナがひっかかっていることが判明。これはNormalizeしてから処理するしかないだろーと考え、やり方を一通り調べてみました。

処理した結果がどうなっているかを調べるために、hexdumpのルーチンをつけています。

Normalizeしてからコード判定してみたら、問題なくできました。

AppleScript名:Unicodeの文字をNormalizeする
– Created 2015-09-30 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–Reference:
–http://akisute.com/2010/05/utf-8-normalize.html
–http://nomenclator.la.coocan.jp/unicode/normalization.htm

set a to “がぎぐげご”
set aStr to current application’s NSString’s stringWithString:a
log hexDumpString(aStr)
–> {”E3″, “81″, “8C”, “E3″, “81″, “8E”, “E3″, “81″, “90″, “E3″, “81″, “92″, “E3″, “81″, “94″}

–NFD
set aNFD to aStr’s decomposedStringWithCanonicalMapping()
–>  (NSString) “がぎぐげご”
log hexDumpString(aNFD)
–> {”E3″, “81″, “8B”, “E3″, “82″, “99″, “E3″, “81″, “8D”, “E3″, “82″, “99″, “E3″, “81″, “8F”, “E3″, “82″, “99″, “E3″, “81″, “91″, “E3″, “82″, “99″, “E3″, “81″, “93″, “E3″, “82″, “99″}

–NFKD
set aNFKD to aStr’s decomposedStringWithCompatibilityMapping()
–>  (NSString) “がぎぐげご”
log hexDumpString(aNFKD)
–> {”E3″, “81″, “8B”, “E3″, “82″, “99″, “E3″, “81″, “8D”, “E3″, “82″, “99″, “E3″, “81″, “8F”, “E3″, “82″, “99″, “E3″, “81″, “91″, “E3″, “82″, “99″, “E3″, “81″, “93″, “E3″, “82″, “99″}

–NFC
set aNFC to aStr’s precomposedStringWithCanonicalMapping()
–>  (NSString) “がぎぐげご”
log hexDumpString(aNFC)
–> {”E3″, “81″, “8C”, “E3″, “81″, “8E”, “E3″, “81″, “90″, “E3″, “81″, “92″, “E3″, “81″, “94″}

–NFKC
set aNFKC to aStr’s precomposedStringWithCompatibilityMapping()
–>  (NSString) “がぎぐげご”
log hexDumpString(aNFKC)
–> {”E3″, “81″, “8C”, “E3″, “81″, “8E”, “E3″, “81″, “90″, “E3″, “81″, “92″, “E3″, “81″, “94″}

–NSStringをhexdumpする
on hexDumpString(theNSString)
  set theNSData to theNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set theString to (theNSData’s |description|()’s uppercaseString())
  
  
–Remove “< " ">” characters in head and tail
  
set tLength to (theString’s |length|()) - 2
  
set aRange to current application’s NSMakeRange(1, tLength)
  
set theString2 to theString’s substringWithRange:aRange
  
  
–Replace Space Characters
  
set aString to current application’s NSString’s stringWithString:theString2
  
set bString to aString’s stringByReplacingOccurrencesOfString:” “ withString:“”
  
  
set aResList to splitString(bString, 2)
  
–> {”E3″, “81″, “82″, “E3″, “81″, “84″, “E3″, “81″, “86″, “E3″, “81″, “88″, “E3″, “81″, “8A”}
  
  
return aResList
  
end hexDumpString

–Split NSString in specified aNum characters
on splitString(aText, aNum)
  
  
set aStr to current application’s NSString’s stringWithString:aText
  
if aStr’s |length|() aNum then return aText
  
  
set anArray to current application’s NSMutableArray’s new()
  
set mStr to current application’s NSMutableString’s stringWithString:aStr
  
  
set aRange to current application’s NSMakeRange(0, aNum)
  
  
repeat while (mStr’s |length|()) > 0
    if (mStr’s |length|()) < aNum then
      anArray’s addObject:(current application’s NSString’s stringWithString:mStr)
      
mStr’s deleteCharactersInRange:(current application’s NSMakeRange(0, mStr’s |length|()))
    else
      anArray’s addObject:(mStr’s substringWithRange:aRange)
      
mStr’s deleteCharactersInRange:aRange
    end if
  end repeat
  
  
return (current application’s NSArray’s arrayWithArray:anArray) as list
  
end splitString

★Click Here to Open This Script 

AppleScript名:Unicodeの文字を各方法でNormalizeして文字数をカウント
– Created 2015-10-02 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

–Reference:
–http://akisute.com/2010/05/utf-8-normalize.html
–http://nomenclator.la.coocan.jp/unicode/normalization.htm

set aRes to normalizeStrAndRetEachLength("がぎぐげご")
–> {origLen:5, NFD:10, NFKD:10, NFC:5, NFKC:5}
set bRes to normalizeStrAndRetEachLength("ぱぴぷぺぽ")
–> {origLen:5, NFD:10, NFKD:10, NFC:5, NFKC:5}
set cRes to normalizeStrAndRetEachLength("㌀")
–> {origLen:1, NFD:1, NFKD:5, NFC:1, NFKC:4}
set dRes to normalizeStrAndRetEachLength("㍿")
–> {origLen:1, NFD:1, NFKD:4, NFC:1, NFKC:4}
set eRes to normalizeStrAndRetEachLength("Å")
–> {origLen:1, NFD:2, NFKD:2, NFC:1, NFKC:1}
set fRes to normalizeStrAndRetEachLength("…")
–> {origLen:1, NFD:1, NFKD:3, NFC:1, NFKC:3}
set gRes to normalizeStrAndRetEachLength("")
–> {origLen:1, NFD:1, NFKD:3, NFC:1, NFKC:3}
set hRes to normalizeStrAndRetEachLength("")
–> {origLen:1, NFD:1, NFKD:2, NFC:1, NFKC:2}
set iRes to normalizeStrAndRetEachLength("")
–> {origLen:1, NFD:1, NFKD:3, NFC:1, NFKC:3}

on normalizeStrAndRetEachLength(a)
  set aStr to current application’s NSString’s stringWithString:a
  
set origLenNum to aStr’s |length|()
  
set aNFD to aStr’s decomposedStringWithCanonicalMapping()’s |length|() –NFD
  
set aNFKD to aStr’s decomposedStringWithCompatibilityMapping()’s |length|() –NFKD
  
set aNFC to aStr’s precomposedStringWithCanonicalMapping()’s |length|() –NFC
  
set aNFKC to aStr’s precomposedStringWithCompatibilityMapping()’s |length|() –NFKC
  
return {origLen:origLenNum, NFD:aNFD, NFKD:aNFKD, NFC:aNFC, NFKC:aNFKC}
end normalizeStrAndRetEachLength

★Click Here to Open This Script 

2015/09/29 多国語OCR「FineReader OCR Pro」をAppleScriptでコントロール

finereader_ocr_pro.jpgMac App Storeで見つけた多国語OCRソフトウェア「FineReader OCR Pro」(バージョン12.1.3)がAppleScriptに対応していたので、いろいろ試してみました(以下、FineReaderと省略)。

FineReaderは英語、日本語をはじめ185もの自然言語に4つの人工言語(エスペラント語とか)、8つの公式言語(簡易化学式とか)などなど、さまざまな言語に対応しており、日本語の認識率もかなりのものです(こんなに認識率の高いOCRソフトは久しぶり)。価格も期間限定で9,800円と非常にリーズナブルです。先日、オープンソースのOCRソフト「tesseract」を試して「こんなもんなのかー」と打ちのめされたばかりなので、なおさらFineReader OCR Proは好印象です。

185言語と書きましたが、単語チェック用の辞書を備えている39の言語(古語や重複分をのぞく)がターゲットといえるでしょう。

開発元の「ABBYY USA Software House Inc.」のサイトからお試し版をダウンロードできる(メールアドレスなどの登録が必要)ので、そちらで試してみました。試用版は30日間あるいは100ページの文字認識を行うと期限満了となるようです。

freader1.png

そんなFineReaderは、なんとAppleScript対応。しかも、AppleScript用語辞書の内容までローカライズされています(内容は各言語に合わせて動的に生成しているもよう)。久しぶりに日本語で書かれたAppleScript用語辞書を見ました。

freader2.png

実に気合いの入ったFinerReader、実際にScriptingを試してみるとどんなもんでしょうか?

まずは、認識言語関係の情報を調べてみました。

AppleScript名:FineReaderで言語情報を取得
tell application “FineReader”
  set anID to get language id from language name “日本語”
  
–> “Japanese”
  
  
set aName to get language name from id anID
  
–> “日本語”
  
  
set aBool to has dictionary support from language with id anID
  
–> true
  
  
set aType to get ocr language group with key anID
  
–> “自然言語”
  
end tell

★Click Here to Open This Script 

日本語のIDは”Japanese”だよ、と親切に教えてくれました。また、日本語が自然言語に分類される言語だということも教えてくれます(C++、Basic、Cobolとかのプログラミング言語も認識言語にリストアップされているが、Objective-CはないしAppleScriptもない)。ちなみに、FineReader OCR Proいわく、C++は「公式言語」という区分のようです(プログラミング言語じゃないんだ、、、)。

ためしに、設定内容を取得してみたところ・・・

AppleScript名:FineReaderの設定内容を取得
tell application “FineReader”
  get preferences
end tell
–> {exportPdfImageQuality:balanced quality, exportPptXKeepPictures:true, exportHtmlImageQuality:balanced quality, exportPdfPrintDocumentSequrity:false, exportFb2ImageQuality:balanced quality, exportPdfCopyDocumentSequrity:false, exportPdfOpenDocumentSequrity:false, exportHtmlTextEncoding:utf8, exportOdtLayout:as editable copy, exportEpubUseFirstPageAsBookCover:true, exportRtfKeepLineNumbers:true, exportEpubKeepPictures:true, exportRtfLayout:as editable copy, exportHtmlUseCSS:true, exportEpubKeywords:{}, exportHtmlTableContext:autocontext, exportOdtHighlightUncertainCharacter:false, exportHtmlCreateFileBasedOnTableContext:false, exportRtfKeepPictures:true, exportCsvInsertPageBreakCharacterAsPageSeparator:false, exportRtfHighlightUncertainCharacter:false, exportOdtKeepPictures:true, exportDocXKeepLineBreaksAndHyphens:false, exportHtmlLayout:as formatted text, exportPdfExportMode:text under image, exportDocXIncreasePaperSizeFitToContent:true, exportOdtKeepLineBreaksAndHyphens:false, exportPdfKeepPageNumbersHeadersAndFooters:true, exportXlsXImageQuality:1, exportPdfCopyDocumentSequrityPassword:”", exportPdfUseMRC:false, exportHtmlKeepTextAndBackgroundColor:false, exportPptXKeepPageNumbersHeadersAndFooters:true, exportPdfPaperMargins:”1.180555, 0.7881944, 0,59027777, 0.7881944″, exportHtmlKeepPictures:true, exportPdfKeepTextAndBackgroundColor:true, exportFb2UseFirstPageAsBookCover:true, exportDocXKeepPageBreaks:true, exportTxtUseBlankLineParagraphSeparator:false, exportEpubBookTitle:”", exportTxtKeepLineBreaksAndHyphens:true, preferencesRecognitionLanguages:”Japanese+English”, exportTxtInsertPageBreakCharacter:false, exportXlsXKeepPictures:true, exportPdfOpenDocumentSequrityPassword:”", exportEpubAnnotation:”", exportTxtCodePage:1251, exportCsvTextCodePage:1251, exportXlsXKeepPageNumbersHeadersAndFooters:true, exportHtmlKeepPageNumbersHeadersAndFooters:true, exportDocXImageQuality:balanced quality, exportHtmlKeepTextLineBreaksAndHyphens:false, exportCsvFieldSeparator:semicolon, exportDocXKeepLineNumbers:true, exportEpubAuthor:”", exportPdfEmbedFonts:true, exportRtfImageQuality:balanced quality, exportImageFormat:2, exportFb2BookTitle:”", exportXlsXConvertNumericalValuesToNumbers:false, exportDocXHighlightUncertainCharacter:false, exportFb2PreserveFontsAndFontsSize:false, exportOdtImageQuality:balanced quality, exportOdtKeepTextAndBackgroundColor:true, exportPdfEnabledTaggedPDF:true, exportDocXPaperMargins:”1.180555, 0.7881944, 0,59027777, 0.7881944″, exportRtfKeepPageNumbersHeadersAndFooters:true, exportTxtTextEncoding:utf8, exportDocXKeepPictures:true, exportPdfKeepPictures:true, exportFormat:to plain text, exportFb2KeepPictures:true, exportImageCompressionType:2, exportRtfKeepLineBreaksAndHyphens:false, exportPptXTextLineBreaksAndHyphens:false, exportTxtKeepPageNumbersHeadersAndFooters:true, exportEpubPreserveFontsAndFontsSize:false, exportCsvTextEncoding:utf8, exportTxtLayout:as plain text, exitAfterExport:false, exportFb2Keywords:{}, exportRtfPaperMargins:”1.180555, 0.7881944, 0,59027777, 0.7881944″, exportDocXLayout:as editable copy, exportSaveOption:0, exportImageQuality:50, exportXlsXIgnoreTextOutsideTables:false, exportDocXPaperSize:automatic, exportEpubLayout:as formatted text, exportPptXImageQuality:balanced quality, exportImageColorMode:0, exportPdfMakePdfACompliant:false, exportOdtPaperMargins:”1.180555, 0.7881944, 0,59027777, 0.7881944″, exportPdfCreateOutline:false, exportRtfIncreasePaperSizeFitToContent:true, exportRtfKeepTextAndBackgroundColor:true, exportEpubEmbedFonts:false, exportOdtPaperSize:automatic, exportOdtKeepPageNumbersHeadersAndFooters:true, exportDocXKeepTextAndBackgroundColor:true, exportSaveMultyPageImage:false, exportFb2Author:”", exportRtfPaperSize:automatic, exportFb2Annotation:”", exportEpubImageQuality:balanced quality, exportPdfPaperSize:automatic, exportRtfKeepPageBreaks:true, exportOdtIncreasePaperSizeFitToContent:true, exportOpenAfterExport:true, exportFb2Layout:as formatted text, exportPptXWrapText:false, exportOdtKeepPageBreaks:true, exportOdtKeepLineNumbers:true, exportHtmlCreateNavigation:true, exportFb2EmbedFonts:false, exportCsvIgnoreTextOutsideTables:0, exportDocXKeepPageNumbersHeadersAndFooters:true, preferencesRecognitionLanguages:”Japanese+English”}

★Click Here to Open This Script 

大量の情報が出てきました。ただし、Read Onlyです(取得コマンドはあっても、設定コマンドがない)。しかも、デバッグ情報っぽい内容であり、誰が何のためにこの情報を使うんだろうと不思議に思いました(せめて、設定できれば ^ー^;;)。

不思議なことに、サンドボックスで実行中かどうかの確認が行えます。一応、Activity Monitorで調べてみるとサンドボックス対応ではありません。

sandbox.png

AppleScript名:FineReaderがSandboxで実行中かを取得
tell application “FineReader”
  set aRes to (is sandboxed)
  
–> false
end tell

★Click Here to Open This Script 

AppleScriptからも属性値を確認してみましたが、やはりSandboxedな状態ではないようです(将来的にそうなる、ということかも?)。

で、指定の画像を文字認識させるべく試してみたのですが・・・なかなか苦労しました(30分ぐらい悩んでしまいました)。

苦労ポイント1:言語指定の方法がわかりにくい

冒頭のAppleScriptで、”日本語”のIDを調べてみたら”Japanese”だといわれたので、パラメータに”Japanese”と指定してみましたが・・・何も認識されませんでした。

いろいろ試してみたところ、Japaneseという予約語があって(おいおいー!!)、そちらで指定するようになっているようです。じゃあ、IDを調べる機能が返してきた文字列はなんなんだ?!(^ー^;;

ほかにも、対象言語が1つでもリストで指定する必要があったりと、いろいろ楽しませてくれます。

苦労ポイント2:認識コマンドが非同期実行

さらに、認識コマンドを実行すると、すぐにAppleScriptが終了してしまいました。認識コマンドが非同期実行されるらしく、OCR処理が終わったかどうかはステータスを監視しないといけないようです。かなり特殊な挙動です。

Mac AppStoreのレビューを読むと、ページ数のあるPDFなどをOCR処理するとけっこうな時間待たされるようなので、そのあたりへの配慮(タイムアウトするよりはいいでしょ? という)なのかもしれません。

Scriptからコントロールできるようになるまで、少々苦労させられましたが、認識率自体は非常に高く、実用性は高いと思われました。

AppleScript名:FineReaderで指定画像を文字認識してデスクトップにテキスト出力
set aFile to (choose file) as string
set outPath to ((path to desktop) as text) & (do shell script “uuidgen”) & “.txt”

tell application “FineReader”
  
  
–Check Ready
  
set getReady to (is finereader controller active)
  
if getReady = false then return
  
  
set idList to {Japanese, English} –認識ターゲット言語
  
–Caution: this command is executed asynchronously
  
export to txt file outPath from file aFile ocr languages enum idList retain layout (as plain text) encoding (utf8)
  
  
–Wait for finish
  
repeat 30 times
    set curStat to (is busy)
    
if curStat = false then exit repeat
    
delay 1
  end repeat
  
end tell

★Click Here to Open This Script 

Keynote書類をキャプチャした画像を認識させてみましたが、そのぐらいだと100%近い認識率で、書類に貼り付けた画像の内容まで認識してしまうほど。本アプリのエンジン部分は非常に高性能です。

ただ、AppleScript用語辞書の内容とかはもうちょっと、実際にAppleScriptを書いている人間からのフィードバックを取り入れたほうがよいと思われます。本ソフトウェア自体は、ひじょうにおすすめです。

2015/09/29 BridgePlus 1.2のframeworkの新機能テスト(1)

Shane StanleyのASObjCExtras.frameworkがAppleScript Library「BridgePlus」に進化するにあたって、同ライブラリのバンドル内にフレームワークが内包される形式に移行しました。

「フレームワークを直接(AppleScriptObjCになじみのない)scripterに使ってもらうのは敷居が高すぎる」とShaneが判断し、ラッパーを介することで使いやすさを増したのが「BridgePlus」である、といえます。BridgePlusではCocoaの値を返すことを極力避け、listやtextなどのScripterになじみ深いデータ型にかならず変換して値を返すように細心の注意が払われています。型変換のためにオーバーヘッドが発生しても、とっつきやすさを優先させたわけです。

ただ、逆をいえばBrdgePlusの本体は内包されているフレームワークであって、Script Libraryはその入れ物(+呼び出し用のハンドラ群+AppleScript用語辞書)にすぎません。

BridgePlusに移行してから、内部のframeworkの機能についてはノーマークだったのですが、先日のIM関連情報取得や状態設定にみられるような「お得な機能」をチェックしておくべきと考え、ひととおり調べてみました(朝のランニングにしてはハードなものになりました)。

結論からいえば、ひじょうに有用性の高い機能がいくつも見つかり・・・個人的には有用な調査が行えました。量が多いので、まだすべてを確認し終わっていませんが、9割ぐらいはカバーできていると思います。

なお、返り値についてはASObjC Explorer 4のCocoaログ機能で表示されたものをそのまま記載しており、NSConcreteValueというのはログのためにASObjC Explorer 4内部で定義しているものであり、実際の内容を示すものではありません(無視してOK)。

アップデート:
Shaneから「resourceValueForKey: forURLsOrFiles:」について正しい呼び出し方について指摘があり、「NSURLを利用するように設計してあり、指定のパス(ディレクトリ)内の各ファイルについてループせずに特定の属性を取得するようになっている」とのこと。

AppleScript名:BrdgePlus 1.2のframeworkの新機能てすと1.1
– Created 2015-09-29 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus” version “1.2″

load framework

– New in v1.2.0

– House-keeping methods

set aRes to BridgePlus’s SMSForder’s frameworkDate()
–>  (NSString) “Sep 23 2015, 15:37:08″

– Regular Expression methods

– Returns the first matching string found. If none found, returns missing value.
set bRes to BridgePlus’s SMSForder’s findFirstMatch:“[98]\\d{6}” inString:“#98158084 Xxxxx Xxxxx xxxx” options:“ix”
–>  (NSString) “9815808″

–Returns the first match record found. If none found, returns missing value.
set cRes to BridgePlus’s SMSForder’s findFirstMatchRecord:“[98]\\d{6}” inString:“#98158084 Xxxxx Xxxxx xxxx” options:“ix”
–>  (NSDictionary) {​​​​​foundRange:(NSConcreteValue) NSRange: {1, 7}, ​​​​​captureGroup:0, ​​​​​foundString:”9815808″​​​}

–Returns an array of the matching strings found. If none found, returns an empty array.
set dRes to BridgePlus’s SMSForder’s findMatches:“[98]\\d{6}” inString:“#98158084 Xxxxx 98000000 xxxx” options:“ix”
–>  (NSArray) {​​​​​”9815808″, ​​​​​”9800000″​​​}

–Returns an array of the match records found. If none found, returns an empty array.
set eRes to BridgePlus’s SMSForder’s findMatchRecords:“[98]\\d{6}” inString:“#98158084 Xxxxx 98000000 xxxx” options:“ix”
–>  (NSArray) {​​​​​{​​​​​​​foundRange:(NSConcreteValue) NSRange: {1, 7}, ​​​​​​​captureGroup:0, ​​​​​​​foundString:”9815808″​​​​​}, ​​​​​{​​​​​​​foundRange:(NSConcreteValue) NSRange: {16, 7}, ​​​​​​​captureGroup:0, ​​​​​​​foundString:”9800000″​​​​​}​​​}

– Returns an array for each match, sorted in your specified capture group order. If you specify non-existent capture groups or capture groups that do not participate in a particular match, they will be represented by missing value. If no matches are found, the overall result will be missing value.
set fRes to BridgePlus’s SMSForder’s findMatches:“[98]\\d{6}” inString:“#98158084 Xxxxx 98000000 xxxx” options:“ix” captureGroups:{0}
–>  (NSArray) {​​​​​{​​​​​​​”9815808″​​​​​}, ​​​​​{​​​​​​​”9800000″​​​​​}​​​}

– Returns an array of match records for each match, sorted in your specified capture group order. If no matches are found, the overall result will be missing value.
set gRes to BridgePlus’s SMSForder’s findMatchRecords:“[98]\\d{6}” inString:“#98158084 Xxxxx 98000000 xxxx” options:“ix” captureGroups:{0}
–>  (NSArray) {​​​​​{​​​​​​​{​​​​​​​​​foundRange:(NSConcreteValue) NSRange: {1, 7}, ​​​​​​​​​captureGroup:0, ​​​​​​​​​foundString:”9815808″​​​​​​​}​​​​​}, ​​​​​{​​​​​​​{​​​​​​​​​foundRange:(NSConcreteValue) NSRange: {16, 7}, ​​​​​​​​​captureGroup:0, ​​​​​​​​​foundString:”9800000″​​​​​​​}​​​​​}​​​}

– Substring extraction methods

set hRes to BridgePlus’s SMSForder’s charactersOfString:“abcdefghijklmnopqrstuvwxyz”
–>  (NSArray) {​​​​​”a”, ​​​​​”b”, ​​​​​”c”, ​​​​​”d”, ​​​​​”e”, ​​​​​”f”, ​​​​​”g”, ​​​​​”h”, ​​​​​”i”, ​​​​​”j”, ​​​​​”k”, ​​​​​”l”, ​​​​​”m”, ​​​​​”n”, ​​​​​”o”, ​​​​​”p”, ​​​​​”q”, ​​​​​”r”, ​​​​​”s”, ​​​​​”t”, ​​​​​”u”, ​​​​​”v”, ​​​​​”w”, ​​​​​”x”, ​​​​​”y”, ​​​​​”z”​​​}

set iRes to BridgePlus’s SMSForder’s wordsOfString:“This is a pen.” – ignore localization
–>  (NSArray) {​​​​​”This”, ​​​​​”is”, ​​​​​”a”, ​​​​​”pen”​​​}

set jRes to BridgePlus’s SMSForder’s localizedWordsOfString:“私の名前は長野谷です。” –consider localization, but not consider proper noun or person’s name
–>  (NSArray) {​​​​​”私”, ​​​​​”の”, ​​​​​”名前”, ​​​​​”は”, ​​​​​”長野”, ​​​​​”谷”, ​​​​​”です”​​​} –{”長野”,”谷”} have to be {”長野谷”} . But it is person’s name.

set kRes to BridgePlus’s SMSForder’s sentencesOfString:“Hello. Goodby. See-you again!” – ignore localization
–>  (NSArray) {​​​​​”Hello. “, ​​​​​”Goodby. “, ​​​​​”See-you again!”​​​}

set lRes to BridgePlus’s SMSForder’s localizedSentencesOfString:“こんにちは。こんばんは。さようなら。えっ?!それはたいへんだ!! 「東京には空がない」と智恵子は言った。”
–>  (NSArray) {​​​​​”こんにちは。”, ​​​​​”こんばんは。”, ​​​​​”さようなら。”, ​​​​​”えっ?!”, ​​​​​”それはたいへんだ!!「”, ​​​​​”東京には空がない」と智恵子は言った。”​​​} — 5th item seems wrong

set mRes to BridgePlus’s SMSForder’s paragraphsOfString:“Hello. Goodby. See-you again!
Hello2. Goodby2. See-you again2!”

–>  (NSArray) {​​​​​”Hello. Goodby. See-you again!”, ​​​​​”Hello2. Goodby2. See-you again2!”​​​}

set nRes to BridgePlus’s SMSForder’s stringsOfString:“xxxx” inString:“#98158084 Xxxxx 98000000 xxxx” options:(current application’s NSCaseInsensitiveSearch)
–>  (NSArray) {​​​​​”Xxxx”, ​​​​​”xxxx”​​​}

set oRes to BridgePlus’s SMSForder’s stringsOfString:“ひよこ” inString:“ひょこっと登場したひよこだよびよこぴよこ” options:(current application’s NSCaseInsensitiveSearch) locale:(current application’s NSLocale’s localeWithLocaleIdentifier:“ja”)
–>  (NSArray) {​​​​​”ひよこ”​​​}

– Substring range methods

set pRes to BridgePlus’s SMSForder’s rangesOfCharactersOfString:“あいうえお”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 1}, ​​​​​(NSConcreteValue) NSRange: {1, 1}, ​​​​​(NSConcreteValue) NSRange: {2, 1}, ​​​​​(NSConcreteValue) NSRange: {3, 1}, ​​​​​(NSConcreteValue) NSRange: {4, 1}​​​}

set qRes to BridgePlus’s SMSForder’s rangesOfCharactersOfString:“This is a pen.”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 1}, ​​​​​(NSConcreteValue) NSRange: {1, 1}, ​​​​​(NSConcreteValue) NSRange: {2, 1}, ​​​​​(NSConcreteValue) NSRange: {3, 1}, ​​​​​(NSConcreteValue) NSRange: {4, 1}, ​​​​​(NSConcreteValue) NSRange: {5, 1}, ​​​​​(NSConcreteValue) NSRange: {6, 1}, ​​​​​(NSConcreteValue) NSRange: {7, 1}, ​​​​​(NSConcreteValue) NSRange: {8, 1}, ​​​​​(NSConcreteValue) NSRange: {9, 1}, ​​​​​(NSConcreteValue) NSRange: {10, 1}, ​​​​​(NSConcreteValue) NSRange: {11, 1}, ​​​​​(NSConcreteValue) NSRange: {12, 1}, ​​​​​(NSConcreteValue) NSRange: {13, 1}​​​}

set rRes to BridgePlus’s SMSForder’s rangesOfLocalizedWordsOfString:“私の名前は長野谷です。”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 1}, ​​​​​(NSConcreteValue) NSRange: {1, 1}, ​​​​​(NSConcreteValue) NSRange: {2, 2}, ​​​​​(NSConcreteValue) NSRange: {4, 1}, ​​​​​(NSConcreteValue) NSRange: {5, 2}, ​​​​​(NSConcreteValue) NSRange: {7, 1}, ​​​​​(NSConcreteValue) NSRange: {8, 2}​​​}

set sRes to BridgePlus’s SMSForder’s rangesOfSentencesOfString:“Hello. Goodby. See-you again!”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 7}, ​​​​​(NSConcreteValue) NSRange: {7, 8}, ​​​​​(NSConcreteValue) NSRange: {15, 14}​​​}

set tRes to BridgePlus’s SMSForder’s rangesOfLocalizedSentencesOfString:“こんにちは。こんばんは。さようなら。えっ?!それはたいへんだ!! 「東京には空がない」と智恵子は言った。”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 6}, ​​​​​(NSConcreteValue) NSRange: {6, 6}, ​​​​​(NSConcreteValue) NSRange: {12, 6}, ​​​​​(NSConcreteValue) NSRange: {18, 4}, ​​​​​(NSConcreteValue) NSRange: {22, 11}, ​​​​​(NSConcreteValue) NSRange: {33, 19}​​​}

set uRes to BridgePlus’s SMSForder’s rangesOfParagraphsOfString:“Hello. Goodby. See-you again!
Hello2. Goodby2. See-you again2!”

–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 29}, ​​​​​(NSConcreteValue) NSRange: {30, 32}​​​}

set vRes to BridgePlus’s SMSForder’s rangesOfLinesOfString:“Hello. Goodby. See-you again!
Hello2. Goodby2. See-you again2!”

–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 29}, ​​​​​(NSConcreteValue) NSRange: {30, 32}​​​}

set wRes to BridgePlus’s SMSForder’s rangesOfString:“xxxx” inString:“#98158084 Xxxxx 98000000 xxxx” options:(current application’s NSCaseInsensitiveSearch)
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {10, 4}, ​​​​​(NSConcreteValue) NSRange: {25, 4}​​​}

set xRes to BridgePlus’s SMSForder’s rangesOfString:“ひよこ” inString:“ひょこっと登場したひよこだよびよこぴよこ” options:(current application’s NSCaseInsensitiveSearch) locale:(current application’s NSLocale’s localeWithLocaleIdentifier:“ja”)
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {9, 3}​​​}

set yRes to BridgePlus’s SMSForder’s indexSetWithArray:{1, 2, 3, 4, 5}
–>  (NSIndexSet) [number of indexes: 5 (in 1 ranges), indexes: (1-5)]

–Miscellaneous methods

set theFiles to current application’s NSFileManager’s defaultManager()’s contentsOfDirectoryAtURL:(Cocoaify (path to desktop)) includingPropertiesForKeys:{current application’s NSURLContentModificationDateKey} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
set zRes to ASify from current application’s SMSForder’s resourceValueForKey:(current application’s NSURLContentModificationDateKey) forURLsOrFiles:theFiles
–>  {​​​​​date “2015年9月24日木曜日 15:02:34″, ​​​​​date “2015年9月18日金曜日 19:19:08″, ​​​​​date “2015年7月29日水曜日 19:08:31″, ​​​​​date “2015年7月20日月曜日 10:44:34″, ​​​​​date “2015年8月21日金曜日 16:49:28″, ​​​​​date “2015年7月30日木曜日 22:43:41″, ​​​​​date “2015年9月5日土曜日 13:15:02″, ​​​​​date “2015年9月29日火曜日 12:31:32″, ​​​​​date “2015年9月5日土曜日 13:28:11″, ​​​​​date “2015年7月30日木曜日 22:29:34″, ​​​​​date “2015年9月16日水曜日 9:33:31″, ​​​​​date “2015年5月30日土曜日 18:53:29″, ​​​​​date “2015年9月16日水曜日 8:57:55″, ​​​​​date “2015年9月12日土曜日 13:40:53″, ​​​​​date “2015年8月28日金曜日 11:42:57″, ​​​​​date “2015年7月14日火曜日 18:10:27″, ​​​​​date “2015年9月28日月曜日 17:12:39″, ​​​​​date “2015年6月19日金曜日 20:18:17″, ​​​​​date “2015年7月18日土曜日 17:35:20″, ​​​​​date “2015年5月10日日曜日 0:01:01″, ​​​​​date “2015年6月22日月曜日 15:46:16″, ​​​​​date “2015年9月16日水曜日 9:08:07″, ​​​​​date “2015年9月5日土曜日 13:14:35″, ​​​​​date “2015年9月16日水曜日 9:02:31″, ​​​​​date “2015年9月29日火曜日 0:11:38″, ​​​​​date “2015年8月18日火曜日 9:58:19″, ​​​​​date “2015年8月28日金曜日 21:23:26″, ​​​​​date “2015年7月18日土曜日 17:35:34″, ​​​​​date “2015年4月23日木曜日 15:21:54″, ​​​​​date “2015年6月3日水曜日 10:05:53″, ​​​​​date “2015年4月23日木曜日 15:21:56″, ​​​​​date “2015年9月26日土曜日 21:10:07″, ​​​​​date “2015年8月31日月曜日 17:29:15″, ​​​​​date “2015年9月22日火曜日 21:19:06″, ​​​​​date “2015年2月14日土曜日 0:30:28″, ​​​​​date “2015年8月24日月曜日 11:28:05″, ​​​​​date “2015年8月22日土曜日 16:52:07″, ​​​​​date “2015年8月14日金曜日 11:27:24″, ​​​​​date “2015年8月14日金曜日 11:27:23″, ​​​​​date “2015年8月14日金曜日 11:06:33″, ​​​​​date “2015年8月14日金曜日 13:13:16″, ​​​​​date “2015年8月31日月曜日 17:08:35″, ​​​​​date “2015年9月12日土曜日 15:45:52″, ​​​​​date “2015年6月22日月曜日 15:18:06″, ​​​​​date “2015年8月18日火曜日 10:01:56″, ​​​​​date “2015年5月21日木曜日 22:15:15″, ​​​​​date “2015年8月18日火曜日 10:06:21″, ​​​​​date “2015年8月20日木曜日 12:52:31″, ​​​​​date “2015年7月10日金曜日 16:01:16″, ​​​​​date “2015年7月14日火曜日 18:18:39″, ​​​​​date “2015年7月16日木曜日 15:22:20″, ​​​​​date “2015年7月16日木曜日 15:33:31″, ​​​​​date “2015年7月16日木曜日 17:01:33″, ​​​​​date “2015年7月18日土曜日 17:28:59″, ​​​​​date “2015年8月10日月曜日 23:13:55″, ​​​​​date “2015年8月14日金曜日 18:08:45″, ​​​​​date “2015年8月25日火曜日 18:47:58″, ​​​​​date “2015年9月8日火曜日 16:22:08″, ​​​​​date “2015年9月2日水曜日 15:04:11″, ​​​​​date “2015年9月6日日曜日 19:53:32″, ​​​​​date “2015年9月15日火曜日 20:49:36″, ​​​​​date “2015年9月21日月曜日 0:44:20″, ​​​​​date “2015年9月28日月曜日 18:45:49″, ​​​​​date “2015年9月28日月曜日 18:46:30″, ​​​​​date “2015年9月29日火曜日 10:38:05″, ​​​​​date “2015年9月29日火曜日 10:40:08″, ​​​​​date “2015年9月29日火曜日 12:29:06″, ​​​​​date “2015年9月29日火曜日 12:29:12″, ​​​​​date “2015年9月29日火曜日 13:53:04″, ​​​​​date “2015年9月21日月曜日 10:20:00″, ​​​​​date “2015年9月21日月曜日 8:55:56″, ​​​​​date “2011年12月29日木曜日 14:30:32″, ​​​​​date “2015年7月12日日曜日 17:53:35″, ​​​​​date “2015年5月19日火曜日 14:43:24″, ​​​​​date “2015年9月15日火曜日 18:30:44″, ​​​​​date “2015年9月29日火曜日 12:25:24″, ​​​​​date “2015年9月29日火曜日 12:27:06″, ​​​​​date “2015年9月29日火曜日 12:28:28″, ​​​​​date “2015年5月25日月曜日 15:59:07″, ​​​​​date “2015年5月25日月曜日 15:59:07″​​​}

★Click Here to Open This Script 

2015/09/28 RealmをAppleScriptから使えるかテスト

CoreDataとかSQLiteの置き換えを目指しているらしいデータベースっぽいプログラム「Realm」。

Mac上ではObjective-CとSwiftしかサポートしていませんが、AppleScriptから使えるのか試してみました(まだ、さわりだけ)。

~/Library/Frameworksに「Realm.framework」を入れてみました。インストール作業はそれだけです。

まずは、ごあいさつ。

AppleScript名:ASOCでRealmの機能を呼び出すテスト
– Created 2015-09-28 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "Realm"

current application’s RLMRealmConfiguration’s defaultConfiguration()’s |path|() as text
–>  "/Users/me/Library/Application Support/au.com.myriad-com.ASObjC-Explorer-4/default.realm"–ASObjC Explorer 4の場合
–> "/Users/me/Library/Application Support/com.apple.ScriptEditor2/default.realm"–Script Editorの場合

★Click Here to Open This Script 

Realm.frameworkの機能を呼び出せている感じがします。

次に、DBの初期化。

AppleScript名:ASOCでRealmの初期化をした雰囲気
– Created 2015-09-28 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Realm”

set |Person| to current application’s RLMRealm’s alloc()’s init()
return |Person|
–>  (RLMRealm)

–スキーマも何も定義されていない(^ー^;

★Click Here to Open This Script 

まだ、初期化してデータベースオブジェクトのインスタンスらしきものを作っただけです。スキーマも何も定義していません。

ためしに、スキーマ定義を聞いてみると、missing valueを返してきます。

AppleScript名:ASOCでRealmの初期化をしてスキーマ定義を聞いてみた
– Created 2015-09-28 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Realm”

set |Person| to current application’s RLMRealm’s alloc()’s init()
|Person|’s schema()
–>  missing value    –スキーマを定義していないので当然(^ー^;

★Click Here to Open This Script 

デフォルトの設定値を取得してみます。

AppleScript名:ASOCでRealmのデフォルト設定値を取得する
– Created 2015-09-28 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Realm”

set aCon to current application’s RLMRealmConfiguration’s defaultConfiguration()
(*
–>  (RLMRealmConfiguration) RLMRealmConfiguration {
  path = /Users/me/Library/Application Support/au.com.myriad-com.ASObjC-Explorer-4/default.realm;
  inMemoryIdentifier = (null);
  encryptionKey = (null);
  readOnly = 0;
  schemaVersion = 0;
  migrationBlock = (null);
  dynamic = 0;
  customSchema = (null);
}
*)

★Click Here to Open This Script 

なんとなく、ドキュメントを読んでみると「リレーションが作れるNSMutableDictionaryのArray」に見えるんですけれど、肝心のスキーマ定義が大変そうです。

ObjCでは、class文を使ってしれっとスキーマ定義をスマートにしているんですが、

AppleScript名:ASOCでRealmのスキーマを使ってみるテスト(未遂)
– Created 2015-09-28 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Realm”

script aPerson
  property parent : current application’s class “RLMObject”
  
  
property fullName : “”
  
property birthdate : “”
  
property numberOfFriends : “”
  
end script

set person to aPerson’s alloc()’s init()

★Click Here to Open This Script 

などと書いても動くわけがないので、なんとなく「プログラムから動的にスキーマ定義する方法があればできそう」な気もしているのですが、まだそこまでたどりついていません。

はたして、、、

2015/09/28 Everyday AppleScriptObjC第3版が発売!

everyday3_300.jpgShane Stanleyによる電子ブック(印刷可能なPDF+サンプルコード)「Everyday AppleScriptObjC」の第3版がリリースされました(全147ページ)。サイトからのダウンロード販売で、価格は2,000円(2015.09.28現在での価格)。

ShaneのAppleScriptObjC本といえば、Xcode上で記述する「AppleScript Explorerd」(第5版)と、Script Editor上で記述する「Everyday AppleScriptObjC」の2つのシリーズがあり、今回のものは後者にあたります。

対応・検証範囲を間もなくリリースされるOS X 10.11「El Capitan」にまで広げ、一般的なAppleScriptユーザーにCocoaの強力な機能を便利に使うやり方をわかりやすく説明しています(英文で書かれていますが、平易な表現であるため、分かりやすいです)。

El Capitanに合わせて大幅に加筆・修正されており(第2版から62ページの加筆:目次含む)、Blocks構文を必要として呼び出せないとばかり思っていた一部のAPIの呼び出し方が提示されていたりと(29章「Out of Bounds」)、第2版まで持っているよというユーザーでも満足できる内容です。

image1.png

現在と未来だけでなく、過去のOS X 10.9, Mavericksのサポートのために「Mavericks or library versions」形式に書き直したサンプルScriptも同梱されています(Yosemite以降用が124本、Mavericks用が118本)。

image2.png
▲LibraryバージョンのScriptで比較。Yosemite以降用の通常のScriptは124本

El Capitan自体は「本来リリースされるはずだったYosemite」「Steve Jobsが生きていたらリリースされていたレベルのYosemite」とも言えるものであり、Yosemiteユーザーの(これまでに例を見ないほどの)急速なEl Capitanへの移行が発生すると思われます。そんな状況下で、早くもEl Capitanの内容を先取りした本書は、Scripter必携の1冊といえるでしょう。

asobjcexp.pngShaneからのお知らせ:El Capitanの発売に合わせ、Cocoaオブジェクトのログなどが可能な唯一のASOC向けエディタ「ASObjC Explorer 4」の特別価格を設定中とのこと。通常価格が$US49.95(日本円で6,000円ぐらい)のところを$US39.95(日本円で5,200円)で発売中とのこと。

2015/09/28 IMの状態を取得・設定する

Shane StanleyのBridgePlus v1.2で新たに実装された「IMまわりの制御を行う機能」を用いて、IMの状態を取得・設定するAppleScriptです。Shane、本当にありがとう! Many Thanks!!

日本語Scripterの長年の懸案事項であった「日本語入力の状態の取得および設定」を、GUI Scriptingを使用せずに行えます。

fig0.png

自分もひそかに「Cocoaの機能を呼び出してできないか?」と独自に(NSTextInputContextあたりを)調べていた最中だったので、Shaneから「こんなのできたよ」と教えてもらった時には腰を抜かしました。

さまざまなアプリケーションの自動化を行うさいに、IMの状態取得と設定は欠くべからざる機能なのですが、GUI Scripting経由で強引に設定するやり方はいまひとつ信頼性が低く(Applet内から実行したときに効かないことがあるのはなんでだろー)、とてつもなく重要な機能の割に長年ASの標準機能に実装もされてきませんでした。

もう、オーストラリアに足を向けて寝られません。カナダ(Mark Alldritt)、アメリカのバーモント州(Bill Cheeseman)にも足を向けて寝られません(クパティーノに足を向けて寝るべき)。

ただ、実験したところ機能が「強力すぎる」点も見受けられ、たとえばフランス語を使わない設定になっている環境でフランス語のIMを呼び出すと・・・呼び出せるうえに、システム環境設定の「言語と地域」にいきなりフランス語が追加されます。このことはよーーく覚えておく必要があります(調子に乗って各種IMを呼び出しまくると、「言語と地域」にいろんな言語が追加されます)。

また、com.apple.inputmethod.ironwoodは音声入力に該当するようですが、これを呼び出しても音声認識による入力状態には移行しませんでした。惜しい、おしすぎる!(^ー^;;

fig1.png

fig2.png

fig3.png

fig4.png

AppleScript名:IMの状態を取得・設定する
– Created 2015-09-16 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePLus : script “BridgePlus” version “1.2″ –1.2より古いバージョン(1.1など)ではこの機能が存在しない

load framework

set y to current application’s SMSForder’s availableInputSourceIDs()
–>  (NSArray) {”com.apple.inputmethod.Kotoeri.Japanese”, “com.apple.inputmethod.Kotoeri.Roman”, “com.apple.inputmethod.Kotoeri.Japanese.Katakana”, “com.apple.inputmethod.Kotoeri”, “com.apple.50onPaletteIM”, “com.apple.inputmethod.ironwood”, “com.apple.CharacterPaletteIM”, “com.apple.KeyboardViewer”}

set y to current application’s SMSForder’s allAvailableInputSourceIDs()
–>  (NSArray) {”com.apple.inputmethod.Kotoeri.Japanese”, “com.apple.inputmethod.Kotoeri.Roman”, “com.apple.inputmethod.Kotoeri.Japanese.Katakana”, “com.apple.inputmethod.Kotoeri”, “com.apple.50onPaletteIM”, “com.apple.inputmethod.ironwood”, “com.apple.CharacterPaletteIM”, “com.apple.KeyboardViewer”, “com.apple.keylayout.US”, “com.apple.SyntheticRomanMode”, “com.apple.keylayout.Czech-QWERTY”, “com.apple.keylayout.Czech”, “com.apple.keylayout.Estonian”, “com.apple.keylayout.Hungarian-QWERTY”, “com.apple.keylayout.Hungarian”, “com.apple.keylayout.Latvian”, “com.apple.keylayout.Lithuanian”, “com.apple.keylayout.PolishPro”, “com.apple.keylayout.Polish”, “com.apple.keylayout.Slovak”, “com.apple.keylayout.Slovak-QWERTY”, “com.apple.keylayout.Bulgarian-Phonetic”, “com.apple.keylayout.Bulgarian”, “com.apple.keylayout.Byelorussian”, “com.apple.keylayout.Macedonian”, “com.apple.keylayout.Russian-Phonetic”, “com.apple.keylayout.Russian”, “com.apple.keylayout.RussianWin”, “com.apple.keylayout.Serbian”, “com.apple.keylayout.Ukrainian-PC”, “com.apple.keylayout.Ukrainian”, “com.apple.keylayout.Colemak”, “com.apple.keylayout.Dvorak-Left”, “com.apple.keylayout.Dvorak-Right”, “com.apple.keylayout.Dvorak”, “com.apple.keylayout.DVORAK-QWERTYCMD”, “com.apple.keylayout.KANA”, “com.apple.keylayout.Australian”, “com.apple.keylayout.Austrian”, “com.apple.keylayout.Belgian”, “com.apple.keylayout.Brazilian-ABNT2″, “com.apple.keylayout.Brazilian”, “com.apple.keylayout.British-PC”, “com.apple.keylayout.British”, “com.apple.keylayout.Canadian-CSA”, “com.apple.keylayout.Canadian”, “com.apple.keylayout.Danish”, “com.apple.keylayout.Dutch”, “com.apple.keylayout.Finnish”, “com.apple.keylayout.French-PC”, “com.apple.keylayout.French-numerical”, “com.apple.keylayout.French”, “com.apple.keylayout.German”, “com.apple.keylayout.Irish”, “com.apple.keylayout.Italian-Pro”, “com.apple.keylayout.Italian”, “com.apple.keylayout.Norwegian”, “com.apple.keylayout.Portuguese”, “com.apple.keylayout.Spanish-ISO”, “com.apple.keylayout.Spanish”, “com.apple.keylayout.Swedish-Pro”, “com.apple.keylayout.Swedish”, “com.apple.keylayout.SwissFrench”, “com.apple.keylayout.SwissGerman”, “com.apple.keylayout.USInternational-PC”, “com.apple.keylayout.2SetHangul”, “com.apple.keylayout.AfghanDari”, “com.apple.keylayout.AfghanPashto”, “com.apple.keylayout.AfghanUzbek”, “com.apple.keylayout.Anjal”, “com.apple.keylayout.Arabic-AZERTY”, “com.apple.keylayout.Arabic-NorthAfrica”, “com.apple.keylayout.Arabic-QWERTY”, “com.apple.keylayout.Arabic”, “com.apple.keylayout.ArabicPC”, “com.apple.keylayout.Armenian-HMQWERTY”, “com.apple.keylayout.Armenian-WesternQWERTY”, “com.apple.keylayout.Azeri”, “com.apple.keylayout.Bangla-QWERTY”, “com.apple.keylayout.Bangla”, “com.apple.keylayout.CangjieKeyboard”, “com.apple.keylayout.Cherokee-Nation”, “com.apple.keylayout.Cherokee-QWERTY”, “com.apple.keylayout.Croatian”, “com.apple.keylayout.Croatian-PC”, “com.apple.keylayout.Devanagari-QWERTY”, “com.apple.keylayout.Devanagari”, “com.apple.keylayout.Faroese”, “com.apple.keylayout.FinnishExtended”, “com.apple.keylayout.FinnishSami-PC”, “com.apple.keylayout.Georgian-QWERTY”, “com.apple.keylayout.Greek”, “com.apple.keylayout.GreekPolytonic”, “com.apple.keylayout.Gujarati-QWERTY”, “com.apple.keylayout.Gujarati”, “com.apple.keylayout.Gurmukhi-QWERTY”, “com.apple.keylayout.Gurmukhi”, “com.apple.keylayout.Hawaiian”, “com.apple.keylayout.Hebrew-QWERTY”, “com.apple.keylayout.Hebrew”, “com.apple.keylayout.Hebrew-PC”, “com.apple.keylayout.Icelandic”, “com.apple.keylayout.Inuktitut-Nunavut”, “com.apple.keylayout.Inuktitut-Nutaaq”, “com.apple.keylayout.Inuktitut-QWERTY”, “com.apple.keylayout.InuttitutNunavik”, “com.apple.keylayout.IrishExtended”, “com.apple.keylayout.Jawi-QWERTY”, “com.apple.keylayout.Kannada-QWERTY”, “com.apple.keylayout.Kannada”, “com.apple.keylayout.Kazakh”, “com.apple.keylayout.Khmer”, “com.apple.keylayout.Kurdish-Sorani”, “com.apple.keylayout.Malayalam-QWERTY”, “com.apple.keylayout.Malayalam”, “com.apple.keylayout.Maltese”, “com.apple.keylayout.Maori”, “com.apple.keylayout.Myanmar-QWERTY”, “com.apple.keylayout.Nepali”, “com.apple.keylayout.NorthernSami”, “com.apple.keylayout.NorwegianExtended”, “com.apple.keylayout.NorwegianSami-PC”, “com.apple.keylayout.Oriya-QWERTY”, “com.apple.keylayout.Oriya”, “com.apple.keylayout.Persian-QWERTY”, “com.apple.keylayout.Persian”, “com.apple.keylayout.Persian-ISIRI2901″, “com.apple.keylayout.Romanian-Standard”, “com.apple.keylayout.Romanian”, “com.apple.keylayout.Sami-PC”, “com.apple.keylayout.Serbian-Latin”, “com.apple.keylayout.Sinhala-QWERTY”, “com.apple.keylayout.Sinhala”, “com.apple.keylayout.Slovenian”, “com.apple.keylayout.SwedishSami-PC”, “com.apple.keylayout.Tamil99″, “com.apple.keylayout.Telugu-QWERTY”, “com.apple.keylayout.Telugu”, “com.apple.keylayout.Thai-PattaChote”, “com.apple.keylayout.Thai”, “com.apple.keylayout.TibetanOtaniUS”, “com.apple.keylayout.Tibetan-QWERTY”, “com.apple.keylayout.Tibetan-Wylie”, “com.apple.keylayout.Turkish-QWERTY-PC”, “com.apple.keylayout.Turkish-QWERTY”, “com.apple.keylayout.Turkish”, “com.apple.keylayout.USExtended”, “com.apple.keylayout.UnicodeHexInput”, “com.apple.keylayout.Urdu”, “com.apple.keylayout.Uyghur”, “com.apple.keylayout.Vietnamese”, “com.apple.keylayout.Welsh”, “com.apple.keylayout.ZhuyinBopomofo”, “com.apple.keylayout.390Hangul”, “com.apple.keylayout.3SetHangul”, “com.apple.keylayout.GJCRomaja”, “com.apple.keylayout.HNCRomaja”, “com.apple.keylayout.WubihuaKeyboard”, “com.apple.keylayout.WubixingKeyboard”, “com.apple.keylayout.ZhuyinEten”, “com.apple.inputmethod.PluginIM”, “com.apple.inputmethod.Ainu”, “com.apple.inputmethod.ink.inkserver”, “com.apple.inputmethod.Korean”, “com.apple.PressAndHold”, “com.apple.inputmethod.SCIM”, “com.apple.inputmethod.AssistiveControl”, “com.apple.inputmethod.Tamil”, “com.apple.inputmethod.TCIM”, “com.apple.inputmethod.ChineseHandwriting”, “com.apple.inputmethod.VietnameseIM”, “com.apple.inputmethod.AinuIM.Ainu”, “com.apple.inputmethod.Kotoeri.Japanese.HalfWidthKana”, “com.apple.inputmethod.Kotoeri.Japanese.FullWidthRoman”, “com.apple.inputmethod.Korean.3SetKorean”, “com.apple.inputmethod.Korean.2SetKorean”, “com.apple.inputmethod.Korean.HNCRomaja”, “com.apple.inputmethod.Korean.390Sebulshik”, “com.apple.inputmethod.Korean.GongjinCheongRomaja”, “com.apple.inputmethod.SCIM.ITABC”, “com.apple.inputmethod.SCIM.WBH”, “com.apple.inputmethod.SCIM.WBX”, “com.apple.inputmethod.Tamil.AnjalIM”, “com.apple.inputmethod.Tamil.Tamil99″, “com.apple.inputmethod.TCIM.WBH”, “com.apple.inputmethod.TCIM.Zhuyin”, “com.apple.inputmethod.TCIM.Cangjie”, “com.apple.inputmethod.TCIM.ZhuyinEten”, “com.apple.inputmethod.TCIM.Jianyi”, “com.apple.inputmethod.TCIM.Pinyin”, “com.apple.inputmethod.VietnameseIM.VietnameseSimpleTelex”, “com.apple.inputmethod.VietnameseIM.VietnameseVNI”, “com.apple.inputmethod.VietnameseIM.VietnameseVIQR”, “com.apple.inputmethod.VietnameseIM.VietnameseTelex”, “jp.monokakido.inputmethod.Kawasemi”, “com.parallels.inputmethod.ParallelsIM”, “com.visionobjects.Axiotron Quickscript.TSC”, “jp.monokakido.inputmethod.Kawasemi.Japanese”, “jp.monokakido.inputmethod.Kawasemi.Japanese.HalfWidthKana”, “jp.monokakido.inputmethod.Kawasemi.Japanese.HalfWidthRoman”, “jp.monokakido.inputmethod.Kawasemi.Roman”, “jp.monokakido.inputmethod.Kawasemi.Japanese.FullWidthRoman”, “jp.monokakido.inputmethod.Kawasemi.Japanese.Katakana”, “jp.monokakido.inputmethod.Kawasemi.Japanese.Code”}

–デモ: IMのメニューで「A」と「あ」を交互に切り替える
repeat 10 times
  set x to current application’s SMSForder’s changeInputSourceTo:“com.apple.inputmethod.Kotoeri.Roman”
  
tell current application
    delay 0.5
  end tell
  
set x to current application’s SMSForder’s changeInputSourceTo:“com.apple.inputmethod.Kotoeri.Japanese”
  
tell current application
    delay 0.5
  end tell
end repeat

★Click Here to Open This Script 

AppleScript名:ironwood(音声認識入力)を呼び出したが、音声入力は始まらなかった
– Created 2015-09-16 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePLus : script “BridgePlus” version “1.2″ –1.2より古いバージョン(1.1など)ではこの機能が存在しない

load framework

tell application “TextEdit” to activate
delay 3
set x to current application’s SMSForder’s changeInputSourceTo:“com.apple.inputmethod.ironwood”

★Click Here to Open This Script 

2015/09/28 NSPredicateによる正規表現を利用した部分一致抽出

Cocoaの機能を用いて、リストのレコードから正規表現による要素の抽出を行うAppleScriptです。実行にはShane StanleyのAppleScript Library「BridgePlus」のインストールが必要になります。

NSPredicateや複数条件を併記するNSCompoundPredicateで、リストに入ったレコードを抽出するのが楽すぎて、もはやこれなしにデータの抽出ができなくなりつつあります。ゴリゴリにPure AppleScriptで記述できないこともないのですが、NSPredicateが使えるだけでもASOCを覚える価値があると思えるほどです。

そんな中、徐々に記述レベルが上がってくると、無茶な抽出にもチャレンジしてみたくなるもので・・・

  「98」で始まる8桁の数字が入った要素をリストアップ。ただし、途中に出現するパターンも含む

という抽出にチャレンジしてみました(仕事で必要だったもので)。完全一致ならなんとか書けるのですが、上記の条件の文字列を「含む」ものまで抽出することを考えると、とたんに難易度が上がります。

結局、AppleのPredicate Programming Guideを総チェックして、「.*」で囲めば「条件に合うものを含む」という状態を抽出できることを見つけました(プログラミングではなくて、情報収集とかパズルのレベルですね)。

AppleScript名:ASOCでNSPredicateによる正規表現を併用した抽出
– Created 2015-09-28 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus” —Shane Stanley’s BridgePlus

set sampleList to {{textData:“Piyomaru”, uID:1}, {textData:“Xx Piyomaru x”, uID:2}, {textData:“xxxxx 11111111 98 x xxxxxxxx.”, uID:3}, {textData:“98x Xxxxxx (xx xxxxxxxxxx)”, uID:4}, {textData:“< < 98158113 >>”, uID:5}, {textData:“#98158084 Xxxxx Xxxxx xxxx”, uID:6}, {textData:“#98158084 Xxxxx Xxxxx xxxx”, uID:7}, {textData:“Office # 98158107″, uID:8}, {textData:“ID#98158087″, uID:9}, {textData:“98158089″, uID:10}, {textData:“00158098″, uID:11}}

–全文一致で抽出
set aRes to my filterRecListByLabel1(sampleList, “textData == ’Piyomaru’”)
–>  {​​​​​{​​​​​​​textData:”Piyomaru”, ​​​​​​​uID:1​​​​​}​​​}

–部分一致で抽出
set bRes to my filterRecListByLabel1(sampleList, “textData contains ’Piyomaru’”)
–>  {​​​​​{​​​​​​​textData:”Piyomaru”, ​​​​​​​uID:1​​​​​}, ​​​​​{​​​​​​​textData:”Xx Piyomaru x”, ​​​​​​​uID:2​​​​​}​​​}

–正規表現で抽出(8桁の数字)
set cRes to my filterRecListByLabel1(sampleList, “textData MATCHES ’\\\\d{8}’”)
–>  {​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}, ​​​​​{​​​​​​​textData:”00158089″, ​​​​​​​uID:11​​​​​}​​​}

set dRes to my filterRecListByLabel1(sampleList, “textData MATCHES ’98\\\\d{6}’”)
–>  {​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}​​​}

set eRes to my filterRecListByLabel1(sampleList, “textData LIKE ’*98??????*’”)
–>  {​​​​​{​​​​​​​textData:”xxxxx 11111111 98 x xxxxxxxx.”, ​​​​​​​uID:3​​​​​}, ​​​​​{​​​​​​​textData:”98x Xxxxxx (xx xxxxxxxxxx)”, ​​​​​​​uID:4​​​​​}, ​​​​​{​​​​​​​textData:”< < 98158113 >>”, ​​​​​​​uID:5​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:6​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:7​​​​​}, ​​​​​{​​​​​​​textData:”Office # 98158107″, ​​​​​​​uID:8​​​​​}, ​​​​​{​​​​​​​textData:”ID#98158087″, ​​​​​​​uID:9​​​​​}, ​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}​​​}

set fRes to my filterRecListByLabel1(sampleList, “textData LIKE ’*\”98\”[0-9][0-9][0-9][0-9][0-9][0-9]*’”) –Oops!!
–>  {}

set gRes to my filterRecListByLabel1(sampleList, “textData LIKE ’*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]*’”) –Oops!!
–>  {}

set hRes to my filterRecListByLabel1(sampleList, “textData MATCHES ’.*[98]\\\\d{6}.*’”) –OK!!
–>  {​​​​​{​​​​​​​textData:”< < 98158113 >>”, ​​​​​​​uID:5​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:6​​​​​}, ​​​​​{​​​​​​​textData:”#98158084 Xxxxx Xxxxx xxxx”, ​​​​​​​uID:7​​​​​}, ​​​​​{​​​​​​​textData:”Office # 98158107″, ​​​​​​​uID:8​​​​​}, ​​​​​{​​​​​​​textData:”ID#98158087″, ​​​​​​​uID:9​​​​​}, ​​​​​{​​​​​​​textData:”98158089″, ​​​​​​​uID:10​​​​​}​​​}

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList as list, aPredicate as string)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to ASify from filteredArray as list
  
return bList
end filterRecListByLabel1

★Click Here to Open This Script 

2015/09/25 spotlightでタグを指定して検索

Cocoaの機能を用いてSpotlight検索で指定フォルダ以下の指定タグのついているファイルを検索するAppleScriptです。

Shane StanleyがAppleScript Users MLに投稿したもので、Spotlightの検索待ち処理でプロパティをループで監視するやり方が採用されていますが、delayの秒数に0.01と指定して済ますあたりが書きこなれています。

実際0.1秒以下の数値指定は無効ではあるものの、delayを使わないとうまく動きません。動いているし、この書き方でエラーを回避できているので、これでいいんでしょう。

複数のPOSIX pathおよび定数で指定されるフォルダをリストにして与え、一括検索を行っているあたりもみどころです。

AppleScript名:spotlightでタグを指定して検索
– Created 2015-09-25 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchIsDone : false

set aRes to my searchInListOfPaths:{POSIX path of (path to home folder)} forTags:{“レッド”}
–>  {{kMDItemPath:”/Users/me/Xxxxxxxxx/XxxxxXxxxxx/XX出撃回数集計(xxxxx)/XXXX_9xxxx_x9(XXXX_XXXX統合版) _9999.9.9XX変更対応.scptd”, kMDItemUserTags:{”レッド”}}, {kMDItemPath:”/Users/me/Xxxxxxxxx/XxxxxXxxxxx/XxxXxxx上のリプレイのダウンロード(alive)/指定IDの戦場の絆のムービーをOffLiberty経由でローカルにダウンロード v3.scpt”, kMDItemUserTags:{”レッド”}}, {kMDItemPath:”/Users/me/Xxxxxxxxx/XxxxxXxxxxx/クリップボードの内容をRTFとPDFとHTMLで書き出す v2.scptd”, kMDItemUserTags:{”レッド”}},…….}

(* scope can be a list of POSIX paths and/or the following enums:
NSMetadataQueryUserHomeScope
NSMetadataQueryLocalComputerScope
NSMetadataQueryNetworkScope
NSMetadataQueryUbiquitousDocumentsScope
NSMetadataQueryUbiquitousDataScope
NSMetadataQueryAccessibleUbiquitousExternalDocumentsScope
NSMetadataQueryIndexedLocalComputerScope — 10.10 or later
NSMetadataQueryIndexedNetworkScope

Eg:
searchInListOfPaths:{current application’s NSMetadataQueryLocalComputerScope} forTags:tagList
*)

–スコープリストの範囲のフォルダから、指定タグを含むファイルをすべてリストアップ
on searchInListOfPaths:scopeList forTags:tagList – pass empty list for any tags
  
  
– Build the search predicate
  
set tagListCount to count of tagList
  
if tagListCount = 0 then – any tags
    set pred to current application’s NSPredicate’s predicateWithFormat:(“kMDItemUserTags LIKE ’*’”)
  else if tagListCount = 1 then
    set pred to current application’s NSPredicate’s predicateWithFormat:“kMDItemUserTags CONTAINS[c] %@” argumentArray:tagList
  else
    set predList to {}
    
repeat with aTag in tagList
      set end of predList to (current application’s NSPredicate’s predicateWithFormat:“kMDItemUserTags CONTAINS[c] %@” argumentArray:{aTag})
    end repeat
    
set pred to current application’s NSCompoundPredicate’s andPredicateWithSubpredicates:predList
  end if
  
  
– make the query
  
set theQuery to current application’s NSMetadataQuery’s alloc()’s init()
  
theQuery’s setPredicate:pred
  
  
– set its scope
  
theQuery’s setSearchScopes:scopeList
  
  
– tell the notification center to tell us when it’s done
  
set notifCenter to current application’s NSNotificationCenter’s defaultCenter()
  
notifCenter’s addObserver:me selector:“searchDone:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:theQuery
  
  
– start searching
  
set my searchIsDone to false
  
theQuery’s startQuery()
  
  
– loop until it’s done
  
repeat
    delay 0.01
    
if searchIsDone then exit repeat
  end repeat
  
  
– get results
  
theQuery’s stopQuery()
  
set theCount to theQuery’s resultCount()
  
set resultsArray to current application’s NSMutableArray’s array() – to hold results
  
repeat with i from 1 to theCount
    (resultsArray’s addObject:((theQuery’s resultAtIndex:(i - 1))’s valuesForAttributes:{current application’s NSMetadataItemPathKey, “kMDItemUserTags”}))
  end repeat
  
  
return resultsArray as list – will be list of records
  
end searchInListOfPaths:forTags:

on searchDone:notification
  set my searchIsDone to true
end searchDone:

★Click Here to Open This Script 

2015/09/25 Remindersのイベントに場所(geofence alarm)を追加する(OS X 10.11版)

Reminders.app(日本語名:リマインダー)の指定イベントに場所(geofence alarm)を追加するAppleScriptのEl Capitan(OS X 10.11)版です。

Yosemite(OS X 10.10.x)のScript Editor上では実行はおろか構文確認(コンパイル)もできないのでご注意ください。

El Capitanのリリースノートで「バグ修正」項目として紹介されている内容の1つで・・・Cocoaの各APIでの定数をAppleScriptにBridgeするようになった、という項目のテスト用にOS X 10.11上で一部修正してみました。

OS X 10.10.x上では、こうした定数があったら、

enums.png

最初の項目が0で、次が1で・・・とあたりをつけて数値で指定するとか、Xcode上でヘッダーファイルを確認して実際の値を調べる・・・という不毛な作業が必要だったわけですが、それがEl Capitanでは解消されるということです。

ただ、目下AppleScriptのプログラムリストの中で、「変数およびサブルーチン名」として色分けされる項目が、とくにCocoaの機能を使うようになってからというもの増えすぎており、可読性が落ちているので、なんとかしてほしいところです。

ちなみに、ASObjC Explorer 4ではこの問題を軽減しており、「サブルーチン名」と「Cocoaのメソッド名」については変数名とは色分けするようになっています(El Capitan上で比較のためにスクリーンショットを撮りましたが、掲載してはいけないことに気づいて削除)。

AppleScript名:Remindersのイベントに場所(geofence alarm)を追加する_10.11
– Created 2014-12-08 by Shane Stanley
– Modified 2015-09-24 by Takaaki Naganoya –Geofence Alarm
– Modified 2015-09-24 by Shane Stanley –Fix the way to Create the geofence alarm
– Modified 2015-09-24 by Takaaki Naganoya –change and test for El Capitan’s Enum bridging

–Reference:
–http://stackoverflow.com/questions/26903847/add-location-to-ekevent-ios-calendar
–http://timhibbard.com/blog/2013/01/03/how-to-create-remove-and-manage-geofence-reminders-in-ios-programmatically-with-xcode/

use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “EventKit”

–Fetch an event to add geofence alarm
tell application “Reminders”
  tell account “iCloud”
    tell list “ホーム” –Use “Home” in English environment or your favorite one
      set theID to id of (last reminder whose completed is false)
    end tell
  end tell
end tell

set theID to text 20 thru -1 of theID

setLocationToLeaveForReminderID(35.745769, 139.675565, “ゲームシティ板橋”, theID, 200)

–指定場所に到着した時に発動するGeofence Alarmを指定のリマインダーに登録する。Geofence半径指定つき(単位:メートル)
on setLocationToEnterForReminderID(aLatitude, aLongitude, aTitle, theID, aRangeByMeter)
  – Get event store
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:(current application’s EKEntityMaskReminder)
  
– Get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
–Create the geofence alarm
  
set enterAlarm to current application’s EKAlarm’s alarmWithRelativeOffset:0
  
enterAlarm’s setProximity:(current application’s EKAlarmProximityEnter)
  
set structLoc to current application’s EKStructuredLocation’s locationWithTitle:aTitle
  
set aLoc to current application’s CLLocation’s alloc()’s initWithLatitude:aLatitude longitude:aLongitude
  
structLoc’s setGeoLocation:aLoc
  
  
– Set radius by meters
  
structLoc’s setRadius:aRangeByMeter
  
enterAlarm’s setStructuredLocation:structLoc
  
  
theReminder’s addAlarm:enterAlarm
  
set aRes to eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
  
return aRes as boolean
end setLocationToEnterForReminderID

–指定場所を出発した時に発動するGeofence Alarmを指定のリマインダーに登録する。Geofence半径指定つき(単位:メートル)
on setLocationToLeaveForReminderID(aLatitude, aLongitude, aTitle, theID, aRangeByMeter)
  – Get event store; 1 means reminders
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:(current application’s EKEntityMaskReminder)
  
– Get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
–Create the geofence alarm
  
set leaveAlarm to current application’s EKAlarm’s alarmWithRelativeOffset:0
  
leaveAlarm’s setProximity:(current application’s EKAlarmProximityLeave)
  
set structLoc to current application’s EKStructuredLocation’s locationWithTitle:aTitle
  
set aLoc to current application’s CLLocation’s alloc()’s initWithLatitude:aLatitude longitude:aLongitude
  
structLoc’s setGeoLocation:aLoc
  
  
– Set radius by meters
  
structLoc’s setRadius:aRangeByMeter
  
leaveAlarm’s setStructuredLocation:structLoc
  
  
theReminder’s addAlarm:leaveAlarm
  
set aRes to eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
  
return aRes as boolean
end setLocationToLeaveForReminderID

★Click Here to Open This Script 

2015/09/24 Remindersのイベントに場所(geofence alarm)を追加する

Reminders.app(日本語名:リマインダー)の指定イベントに場所(geofence alarm)を追加するAppleScriptです。

リマインダーのAppleScript用語辞書では「場所」についてはまったくサポート機能が用意されておらず(El CapitanのRemindersでもサポートなし)、全世界のScripterが肩を落としたものでした(Apple純正アプリのAppleScript用語辞書の残念さは本当に残念です。Finderのタグなど登場して相当の時間が経っているのに機能が追加されていません。タグについてはASOCで直接操作できるので、Appleの対応をもはや期待していない昨今)。

rem5.png
▲リマインダー(Reminders.app)のAppleScript用語辞書にはLocation関連の機能はない

そこで、出来のよくないリマインダーをアテにせず、Cocoaの機能を呼び出して、AppleScriptだけで「場所」の情報を追加するAppleScriptを書いてみました(Shane Stanleyの添削済み)。

この「場所」については、若干の説明が必要です。

geofence.png

緯度・経度でピンポイントの場所を指定できますが、ピンポイントすぎるのと誤差を許容するため「だいたいこのあたり」といった範囲の指定が必要になります。これを「Geofence」と呼ぶようです。

このピンポイントの場所から「半径XXXXメートル以内」のGeofenceに入った場合のアラーム(Enter Alarm)と、出た場合のアラーム(Leave Alarm)の2種類のうちどちらかを設定できるとのこと。

逆に、入るとか出るとかいうアクション指定のないものも作れますが、これがどういう挙動をするのかいまひとつ分かりません(Geofence内でずっと何かの通知が出る?)。

rem1.png
▲テスト用に作ったリマインダー項目

rem2.png
▲初期状態では何も「場所」関連の情報はついていません

rem3.png
▲本Script実行後の状態

rem4.png
▲内容を確認すると、意図したとおり「ゲームシティ板橋」のGeofenceが追加

img_2822_resized.png
▲ゲームシティ板橋店

AppleScript名:Remindersのイベントに場所(geofence alarm)を追加する
– Created 2014-12-08 by Shane Stanley
– Modified 2015-09-24 by Takaaki Naganoya
– Modified 2015-09-24 by Shane Stanley

–Reference:
–http://stackoverflow.com/questions/26903847/add-location-to-ekevent-ios-calendar
–http://timhibbard.com/blog/2013/01/03/how-to-create-remove-and-manage-geofence-reminders-in-ios-programmatically-with-xcode/

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “EventKit”

–Fetch an event to add geofence alarm
tell application “Reminders”
  tell account “iCloud”
    tell list “ホーム” –Use “Home” in English environment or your favorite one
      set theID to id of (last reminder whose completed is false)
      
–>  ”x-apple-reminder://868467FD-1F90-47EF-A9B1-A39334341D41″
    end tell
  end tell
end tell

set theID to text 20 thru -1 of theID
–> “868467FD-1F90-47EF-A9B1-A39334341D41″

setLocationToEnterForReminderID(35.745769, 139.675565, “ゲームシティ板橋”, theID, 200)

on setLocationToEnterForReminderID(aLatitude, aLongitude, aTitle, theID, aRangeByMeter)
  – Get event store; 1 means reminders
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:2 –Deprecated in OS X 10.9 ?
  
(* Enums: { EKEntityMaskEvent:1, EKEntityMaskReminder:2} *)
  
  
– Get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
  
–Create the geofence alarm
  
set enterAlarm to current application’s EKAlarm’s alarmWithRelativeOffset:0 –ここがキモ
  
  
enterAlarm’s setProximity:1 – EKAlarmProximityEnter
  
(* Enums: { EKAlarmProximityNone:0, EKAlarmProximityEnter:1, EKAlarmProximityLeave:2 } *)
  
  
set structLoc to current application’s EKStructuredLocation’s locationWithTitle:aTitle
  
set aLoc to current application’s CLLocation’s alloc()’s initWithLatitude:aLatitude longitude:aLongitude
  
structLoc’s setGeoLocation:aLoc
  
  
– Set radius by meters
  
structLoc’s setRadius:aRangeByMeter –by meters
  
enterAlarm’s setStructuredLocation:structLoc
  
  
theReminder’s addAlarm:enterAlarm
  
set aRes to eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
  
return aRes as boolean
end setLocationToEnterForReminderID

on setLocationToLeaveForReminderID(aLatitude, aLongitude, aTitle, theID, aRangeByMeter)
  – Get event store; 1 means reminders
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:2 –Deprecated in OS X 10.9 ?
  
(* Enums: { EKEntityMaskEvent:1, EKEntityMaskReminder:2} *)
  
  
– Get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
  
–Create the geofence alarm
  
set enterAlarm to current application’s EKAlarm’s alarmWithRelativeOffset:0 –ここがキモ
  
  
enterAlarm’s setProximity:2 – EKAlarmProximityLeave
  
(* Enums: { EKAlarmProximityNone:0, EKAlarmProximityEnter:1, EKAlarmProximityLeave:2 } *)
  
  
set structLoc to current application’s EKStructuredLocation’s locationWithTitle:aTitle
  
set aLoc to current application’s CLLocation’s alloc()’s initWithLatitude:aLatitude longitude:aLongitude
  
structLoc’s setGeoLocation:aLoc
  
  
– Set radius by meters
  
structLoc’s setRadius:aRangeByMeter –by meters
  
enterAlarm’s setStructuredLocation:structLoc
  
  
theReminder’s addAlarm:enterAlarm
  
set aRes to eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
  
return aRes as boolean
end setLocationToLeaveForReminderID

★Click Here to Open This Script 

2015/09/22 クリップボードの内容をRTFとPDFとHTMLで書き出す

Cocoaの機能を用いて、クリップボードの内容をデスクトップにRTFとPDFで書き出すAppleScriptの改訂版です。

前バージョンのAppleScriptでは、スタイル付きテキスト(NSAttributedString)をHTMLに書き出すと、HTML内のタグでは「UTF-8」になっていながらも、HTMLのテキスト自体のエンコーディングがShift JISというたいへん不思議な状態になっていました。

スタイル付きテキスト(NSAttributedString)の取得部分に問題がないことを確認。スタイル付きテキストをPDFやRTFに書き出した場合には問題なし。HTMLの書き出しに固有の根の深い問題があるのかと疑っていましたが・・・難易度でいえば、それほど難しくない話でした。

修正部分はたった1箇所。最後にHTMLをファイルに書き込む際に、UTF-8(NSUTF8StringEncoding)を指定する記述を追加しただけです。

テキストエンコーディングの省略時にはかならず「UTF-8」が指定されるものとばかり思っていましたが、明示的に指定しないとダメということがよくわかりました。

AppleScript名:クリップボードの内容をRTFとPDFとHTMLで書き出す v2
– Created 2015-09-20 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit” – for NSPasteboard, which is the clipboard

–クリップボードの内容をNSAttributedStringに
set anAttr to my getClipboardASStyledText()

–保存先とファイル名を求める
set targFol to POSIX path of (path to desktop)
set aUUID to current application’s NSUUID’s UUID()’s UUIDString() as text

set aRes to my saveStyledTextAsRTF(aUUID, targFol, anAttr) –RTFで書き出す
set bRes to my saveStyledTextAsPDF(aUUID, targFol, anAttr) –PDFで書き出す
set cRes to my saveStyledTextAsHTML(aUUID, targFol, anAttr) –HTMLで書き出す

– クリップボードの内容をNSAttributedStringとして取り出して返す
on getClipboardASStyledText()
  set theNSPasteboard to current application’s NSPasteboard’s generalPasteboard()
  
set theAttributedStringNSArray to theNSPasteboard’s readObjectsForClasses:({current application’s NSAttributedString}) options:(missing value)
  
set theNSAttributedString to theAttributedStringNSArray’s objectAtIndex:0
  
return theNSAttributedString
end getClipboardASStyledText

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsRTF(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings to RTF
  
set bstyledLength to aStyledString’s |string|()’s |length|()
  
set bDict to current application’s NSDictionary’s dictionaryWithObject:“NSRTFTextDocumentType” forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“rtf”
  
  
return (bRTF’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsRTF

–スタイル付きテキストを指定フォルダ(POSIX path)にPDFで書き出し
on saveStyledTextAsPDF(aFileName, targFol, aStyledString)
  – get page size being used for printing
  
set printInfo to current application’s NSPrintInfo’s sharedPrintInfo()
  
set pageSize to printInfo’s paperSize()
  
set theLeft to printInfo’s leftMargin()
  
set theTop to printInfo’s topMargin()
  
  
– make a text view
  
set theView to current application’s NSTextView’s alloc()’s initWithFrame:{origin:{x:0, y:0}, |size|:pageSize}
  
theView’s setTextContainerInset:{theLeft, theTop}
  
  
– put in the text
  
theView’s textStorage()’s setAttributedString:aStyledString
  
set theData to theView’s dataWithPDFInsideRect:{origin:{x:0, y:0}, |size|:pageSize}
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“pdf”
  
  
return (theData’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsPDF

–スタイル付きテキストを指定フォルダ(POSIX path)にHTMLで書き出し
on saveStyledTextAsHTML(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings toHTML
  
set theNSDictionary to current application’s NSMutableDictionary’s dictionaryWithObject:(current application’s NSHTMLTextDocumentType) forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set theNSData to aStyledString’s dataFromRange:{location:0, |length|:aStyledString’s |length|()} documentAttributes:theNSDictionary |error|:(missing value)
  
set aHTML to current application’s NSString’s alloc()’s initWithData:theNSData encoding:(current application’s NSUTF8StringEncoding)
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“html”
  
  
return (aHTML’s writeToFile:thePath atomically:true encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)) as boolean
  
end saveStyledTextAsHTML

★Click Here to Open This Script 

2015/09/21 iOSデバイスからMac上のAppleScriptを実行するEntangler、前バージョンからのアップデート点

昨日、FAXの動作確認のためにきわめて少ない手数でAppleScriptのリモート実行を行うのに使ったEntangler。

前バージョンである「Otto’s Antenna」ではいくつか問題があったわけですが、それがバージョン2.0であるEntanglerではどうなっているでしょうか?

ent1.png

(1)日本語のファイル名のScriptを登録するとおかしくなるバグ

Otto’s Antennaでは、日本語のファイル名のAppleScriptを登録すると、「こんにちは」と「こんにちは~」の2つのファイルが登録されたように見え、「こんにちは~」のほうは実行できないゴーストでした。

実際に日本語のファイル名のAppleScriptを登録してみたところ、この前バージョンで発生していたバグは再発しませんでした。

(2)AppleScriptに引数を渡せない

Otto’s Antennaでは、Shell ScriptなどにArgumentsを渡せるのに、AppleScriptにはArgumentsを渡せない仕様になっていました。

Entanglerで実験してみたところ、iOSデバイス上のEntanglerでScriptボタンを長押しすると、Argumentsの入力ダイアログが表示されます。ダイアログにArgumentsを入力すると、たしかにAppleScript側でArgumentsを受信できました。

AppleScript名:ダイアログてすと
on run anArg
  display dialog (anArg as string)
end run

★Click Here to Open This Script 

昨日のFAX送信実験でいえば、送信先のFAX番号を引数に渡せるようにすることも可能です。

img_3135.PNG

dialog3.png

(3)Scriptが実行されるまでの時間が長い

Otto’s AntennaのときにはKeynoteのスライドをめくるような用途にはちょっと使えないぐらい応答に時間がかかっていました。iPhone上で操作してからMac上でAppleScriptが実行されるまで、だいたい10秒ぐらいかかっていました。

Entanglerでは、ずいぶん早くなりました。1〜2秒といったところでしょうか。Keynoteのスライドめくりには依然としてちょっと物足りないかもしれませんが、応答速度の向上は歓迎できます。

Otto’s Antennaのときにダメだと感じた点が、ずいぶん改善されています。まだEntanglerのすべての機能を試せていませんが、現時点でも満足できています。

2015/09/20 iPhoneから自宅/会社のMac上のAppleScriptを呼び出すEntangler

以前、Otto’s Antennaという名前で紹介したアプリケーションがバージョンアップして、「Entangler」という名前になっていました。

ちょうど、奥方様の実家のFAXの動作確認をする必要があったので、MacにFAXモデムをつないで、指定の電話番号にFAX出力するAppleScriptを書いて、iPhone上のEntanglerから呼び出してみました。

EntanglerというアプリケーションのMac版をMacにインストールし、iOS版をiPhoneにインストールして、iOS側からMac側の機能を呼び出すというのが、基本的な運用スタイルです(AppleWatch版もあるとは知りませんでした)。

前バージョンであるOtto’s Antennaよりもデザインがよくなって、iOSデバイスから操作してからの反応が速くなっていました。EntanglerはAppleScriptのほかにもAutomatorアクションやshell scriptなども実行できるそうです(AppleScriptしかテストしていないですけれども)。

Scriptを実行するMacはSleepせずに、ネットワーク(LAN/WiFi)に接続した状態にしておく必要があります。

print1.png
▲MacBook ProにUSRoboticsのUSB FAX Modem(OS X 10.10.5でも使える)をつなぎ、Sleepしないようにしてスタンバイ

print2.png
▲FAX送信元の電話番号に「ps-ax」と設定(最初に入っていた状態そのまま)

print3.png
▲Mac上のEntanglerにFAX送信するAppleScriptを登録

AppleScript名:print_a_fax
– Created 2015-09-20 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

property aPrinter : “USB Modem”
property targFaxNum : “03-XXXX-XXXX”

tell application “Safari”
  tell front document
    set aPrintSetting to {copies:1, starting page:1, ending page:9999, target printer:aPrinter, fax number:targFaxNum}
    
print with properties aPrintSetting without print dialog
  end tell
end tell

★Click Here to Open This Script 

print4.jpg
▲iPhone上のEntanglerを起動して、「print_a_fax」Scriptを実行

img_3127_resized.png
▲FAX受信中(「ps-ax」と通知が出ている)

63db476c-a6a1-4a82-8650-c79152570edd_resized.png
▲FAX受信中…

ptint0.png
▲Scriptの実行が終了すると、Macの通知センターに通知が表示される

bb1fb10f-5f86-43ed-bd72-fed8ec168e61_resized.png
▲確認のためにFAXで印刷したYahoo!のトップページ。印刷が途切れているのは、本来A4用紙が必要なところに無理やり別のサイズ(たぶんB5)の紙を突っ込んだため

2015/09/20 クリップボードの内容をRTFとPDFで書き出す

Cocoaの機能を用いて、クリップボードの内容をデスクトップにRTFとPDFで書き出すAppleScriptです。

HTMLでも書き出せるのですが、日本語の文字化け(UTF-8で書き出したつもりがShift JISになっていた問題)が解決されていないため、とりあえずこんなもんで。

AppleScript名:クリップボードの内容をRTFとPDFで書き出す
– Created 2015-09-20 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit” – for NSPasteboard, which is the clipboard

–クリップボードの内容をNSAttributedStringに
set anAttr to my getClipboardASStyledText()

–保存先とファイル名を求める
set targFol to POSIX path of (path to desktop)
set aUUID to current application’s NSUUID’s UUID()’s UUIDString() as text

set aRes to my saveStyledTextAsRTF(aUUID, targFol, anAttr) –RTFで書き出す
set bRes to my saveStyledTextAsPDF(aUUID, targFol, anAttr) –PDFで書き出す

– クリップボードの内容をNSAttributedStringとして取り出して返す
on getClipboardASStyledText()
  set theNSPasteboard to current application’s NSPasteboard’s generalPasteboard()
  
set theAttributedStringNSArray to theNSPasteboard’s readObjectsForClasses:({current application’s NSAttributedString}) options:(missing value)
  
set theNSAttributedString to theAttributedStringNSArray’s objectAtIndex:0
  
return theNSAttributedString
end getClipboardASStyledText

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsRTF(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings to RTF
  
set bstyledLength to aStyledString’s |string|()’s |length|()
  
set bDict to current application’s NSDictionary’s dictionaryWithObject:“NSRTFTextDocumentType” forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict
  
  – build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“rtf”
  
  return (bRTF’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsRTF

–スタイル付きテキストを指定フォルダ(POSIX path)にPDFで書き出し
on saveStyledTextAsPDF(aFileName, targFol, aStyledString)
  – get page size being used for printing
  
set printInfo to current application’s NSPrintInfo’s sharedPrintInfo()
  
set pageSize to printInfo’s paperSize()
  
set theLeft to printInfo’s leftMargin()
  
set theTop to printInfo’s topMargin()
  
  – make a text view
  
set theView to current application’s NSTextView’s alloc()’s initWithFrame:{origin:{x:0, y:0}, |size|:pageSize}
  
theView’s setTextContainerInset:{theLeft, theTop}
  
  – put in the text
  
theView’s textStorage()’s setAttributedString:aStyledString
  
set theData to theView’s dataWithPDFInsideRect:{origin:{x:0, y:0}, |size|:pageSize}
  
  – build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“pdf”
  
  return (theData’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsPDF

★Click Here to Open This Script 

2015/09/19 Notes上の指定のエントリをHTMLとして書き出す

Cocoaの機能を使って、Notes(日本語名:「メモ」)の指定エントリの内容をHTMLとして書き出すAppleScriptです。

ただ、普通にPDFやRTFと同様に出力してみたら・・・文字化けしてしまいました(汗)。

html1.png

UTF-8で出力しているはずなのですが、

html2.png

Safariで文字エンコーディングを「日本語(Shift JIS)」に設定すると・・・

html3.png

html4.png

文字化けしなくなります。

html5.png

保存されたHTMLのソースを見ると、エンコーディングはUTF-8となっているのですが、ソースをUTF-8でオープンしても文字化けしたままです。

AppleScript名:Notes上の指定のエントリをHTMLとして書き出す
– Created 2015-09-18 by Shane Stanley
– Modified 2015-09-19 by Takaaki Naganoya
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set targFol to POSIX path of (path to desktop)

tell application "Notes"
  set theBody to body of note 2
  
set theName to name of note 2 – for file name
end tell

– Notesの本文をスタイル付きテキストに
set anNSString to current application’s NSString’s stringWithString:theBody
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF16StringEncoding)
set styledString to current application’s NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)

set cRes to my saveStyledTextAsHTML(theName, targFol, styledString)
–> true

–スタイル付きテキストを指定フォルダ(POSIX path)にHTMLで書き出し
on saveStyledTextAsHTML(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings toHTML
  
set theNSDictionary to current application’s NSMutableDictionary’s dictionaryWithObject:(current application’s NSHTMLTextDocumentType) forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set theNSData to aStyledString’s dataFromRange:{location:0, |length|:aStyledString’s |length|()} documentAttributes:theNSDictionary |error|:(missing value)
  
  
set aHTML to current application’s NSString’s alloc()’s initWithData:theNSData encoding:(current application’s NSUTF8StringEncoding)
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:"/" withString:"_"
  
set theName to theName’s stringByReplacingOccurrencesOfString:":" withString:"_"
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:"html"
  
  
return (aHTML’s writeToFile:thePath atomically:true) as boolean
  
end saveStyledTextAsHTML

★Click Here to Open This Script 

一応、Notes(メモ)から取得した本文データがHTMLだったので、Styled Textに変換しないでそのまま保存するバージョンも作ってみました。こちらであれば、文字化けは見られませんが・・・Styled Text(NSAttributedString)をHTMLに変換してファイル出力するという機能は、普通に文字化けせずに使えてほしいので、回避策を検討しておきたいところです。

AppleScript名:Notes上の指定のエントリをHTMLとして書き出す v2
– Created 2015-09-18 by Shane Stanley
– Modified 2015-09-19 by Takaaki Naganoya
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set targFol to POSIX path of (path to desktop)

tell application "Notes"
  set theBody to body of note 2
  
set theName to name of note 2 – for file name
end tell

– Notesの本文をスタイル付きテキストに
set anNSString to current application’s NSString’s stringWithString:theBody
–>  (NSString) "<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;">ASOCでできないこと<div><br></div><div>CFString の文字列(Cの文字列)を扱えない</div><div><br></div><div>block構文を必要とするAPIを呼べない</div><div><br></div><div>Key Value Observation を使えない(Xcode上ではOK?)</div></body></html>"

set aRes to my saveStyledTextAsHTML(theName, targFol, anNSString)

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsHTML(aFileName, targFol, anNSString)
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:"/" withString:"_"
  
set theName to theName’s stringByReplacingOccurrencesOfString:":" withString:"_"
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:"html"
  
  
return (anNSString’s writeToFile:thePath atomically:true) as boolean
  
end saveStyledTextAsHTML

★Click Here to Open This Script 

2015/09/18 Notes上の指定のエントリをPDFやRTFとして書き出す

Cocoaの機能を使って、Notes(日本語名:「メモ」)の指定エントリの内容をPDFやRTFとして書き出すAppleScriptです。

  「・・・そんなもん作ってどうするんだ、本当に必要なのか?」

と、私自身が感じた内容ではありましたが、Shaneが質問に答えてMLに投稿し、それを私が日本語環境で検証して「文字化けするよー」とメールで報告してやりとりして、「ああ、Encodingがちがうんだー」ということでめでたく動くようになりました(NotesがデータをUTF-8ではなくUTF-16で保持していたのが原因ですが・・・El Capitanの新形式=iOS9上の新形式 だとどーなんだろう???)。

動くようになってはみたものの、あまりに特定の目的に最適化されたScriptだったので、データ取得部分と保存部分を別々に分け、スタイル付きテキストをPDFやRTFとして保存できるルーチンに整備して汎用性を確保しておきました。こういうのを作っておくと、あとになって地道に効いてくるはずです。たぶん・・・きっと。

実行すると、お手元の環境の「メモ」アプリの最新から2つ目のエントリの名称をファイル名にして、本文内容をデスクトップにPDFとRTFで保存します。

notes_entry.png

filenames.png

pdf.png

rtf.png

AppleScript名:Notes上の指定のエントリをPDFやRTFとして書き出す
– Created 2015-09-18 by Shane Stanley
– Modified 2015-09-18 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set targFol to POSIX path of (path to desktop)

tell application “Notes”
  set theBody to body of note 2
  
set theName to name of note 2 – for file name
end tell

– Notesの本文をスタイル付きテキストに
set anNSString to current application’s NSString’s stringWithString:theBody
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF16StringEncoding) –日本語の環境&内容で検証ずみ。Chineseとかはどうか?
set styledString to current application’s NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)

set aRes to my saveStyledTextAsPDF(theName, targFol, styledString)
–> true

set bRes to my saveStyledTextAsRTF(theName, targFol, styledString)
–> true

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsRTF(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings to RTF
  
set bstyledLength to aStyledString’s |string|()’s |length|()
  
set bDict to current application’s NSDictionary’s dictionaryWithObject:“NSRTFTextDocumentType” forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“rtf”
  
  
return (bRTF’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsRTF

–スタイル付きテキストを指定フォルダ(POSIX path)にPDFで書き出し
on saveStyledTextAsPDF(aFileName, targFol, aStyledString)
  – get page size being used for printing
  
set printInfo to current application’s NSPrintInfo’s sharedPrintInfo()
  
set pageSize to printInfo’s paperSize()
  
set theLeft to printInfo’s leftMargin()
  
set theTop to printInfo’s topMargin()
  
  
– make a text view
  
set theView to current application’s NSTextView’s alloc()’s initWithFrame:{origin:{x:0, y:0}, |size|:pageSize}
  
theView’s setTextContainerInset:{theLeft, theTop}
  
  
– put in the text
  
theView’s textStorage()’s setAttributedString:aStyledString
  
set theData to theView’s dataWithPDFInsideRect:{origin:{x:0, y:0}, |size|:pageSize}
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“pdf”
  
  
return (theData’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsPDF

★Click Here to Open This Script 

2015/09/16 指定URLをロード WKWebView版

Cocoaの機能を用いて指定URLのページの内容を読み込んで、ページのタイトルを取得するAppleScriptです。

先のオフラインレンダラーのAppleScriptをMLに投稿してみたところ、ShaneよりYosemiteならWkWebViewが新たにWebViewの後継として用意されているので試してはどうか? という指摘があり、(知らなかったので)慌てて調べた次第です。

通常のAppleScriptでCocoaの機能が使えるといっても、さすがにKey Value Observation(キー値監視)はサポートしていないので、 そこはloading()プロパティをループで確認しています。ただ、このシンプルさゆえにフォアグランド実行(Control-Command-R)させる必要がありません。

一応、これで指定URLの内容は取得できているようなのですが、いまだその内容を画像化はできていません。いまのところ、綺麗な800×600ドットのサイズのグレーの画像が取得できています(つまり、明らかに失敗している)。

# El Capitanで動かしてみたら、実行が終了しませんでした。

AppleScript名:ASOCで指定URLをロード_WKWebView版
– Created 2015-09-16 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “WebKit”

property theWebView : missing value

set aURL to “http://www.apple.com/us/shop/browse/home/specialdeals/mac”
set aRes to getPage(aURL)
set aTitle to (theWebView)’s title()
return aTitle as text
–>  ”Refurbished Mac - Apple Certified - Apple”

–Download the URL page to WkWebView
on getPage(aURL)
  set thisURL to current application’s |NSURL|’s URLWithString:aURL
  
set theRequest to current application’s NSURLRequest’s requestWithURL:thisURL
  
  
set aConf to current application’s WKWebViewConfiguration’s alloc()’s init()
  
  
set (my theWebView) to current application’s WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 800, 600)) configuration:aConf –フレームの大きさは根拠レス
  (
my theWebView)’s setNavigationDelegate:me
  (
my theWebView)’s loadRequest:theRequest
  
  
set waitLoop to 1000 * 60 –60 seconds
  
  
set hitF to false
  
repeat waitLoop times
    set aLoadF to ((my theWebView)’s estimatedProgress()) as number
    
if aLoadF = 1.0 then
      set hitF to true
      
exit repeat
    end if
    
current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
  
return hitF
end getPage

★Click Here to Open This Script 

2015/09/16 指定URLをロードして1枚ものの大きなPNGにレンダリング v2

Cocoaの機能を用いて、指定URLのページを読み込み、1枚ものの大きなPNGにレンダリングするAppleScriptです。

デスクトップ上に「outIMG_1.png」の名前で書き出します。実行には、Script Editor上でControl-Command-Rによるフォアグラウンド実行する必要があります。

v1よりはいくらか実際のSafariに近づいた気がしますが、本物のSafariと同じにはなっていません。WebKitが登場したての頃のCocoaプログラマーの方々の足跡(昔の記録)を追って、いろいろ試行錯誤しています。若干、「車輪の再発明」みたいな・・・

改良点:
・User AgentをSafariにそろえ、Safariのフリをした(v1で実装)
・表示Style Sheetを画面表示用のものが適用されるようにした
・Webコンテンツのサイズから出力エリアの自動判定を行うようにした
・背景色を塗るように指定(ただ、効いていないような)
・PDFに印刷するやり方ではなく、WebViewを画像にするやり方に変更(フッター/ヘッターの体裁がSafariの画面表示イメージに近くなった)

問題点:
・背景色の塗りが正しく反映されていない(とくに、フッター/ヘッダー)
・El Capitanで実行するとレンダリング結果がぼっこぼこ(T_T)

outimg_1_resized.png
▲本AppleScriptでレンダリングしたPNG

render1.png
▲同じページをSafariで表示させたところ

AppleScript名:ASOCで指定URLをロードして1枚ものの大きなPNGにレンダリング v2
– Created 2015-09-15 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “WebKit”
use framework “Quartz”
use framework “AppKit”

property loadDone : false
property theWebView : missing value
property userAgentName : “Version/9.0 Safari/601.1.56″

set aOutPath to “~/Desktop/outIMG_1.png”
–set aURL to “http://www.yahoo.co.jp”
set aURL to “http://www.apple.com/jp/shop/browse/home/specialdeals/mac”
–set aURL to “http://piyocast.com/as”

–Check If this script runs in foreground
if not (current application’s NSThread’s isMainThread()) as boolean then
  display alert “This script must be run from the main thread (Command-Control-R in Script Editor).” buttons {“Cancel”} as critical
  
error number -128
end if

set aPath to current application’s NSString’s stringWithString:aOutPath
set bPath to aPath’s stringByExpandingTildeInPath()

–URL Validation check
set aRes to validateURL(aURL)
if aRes = false then return
set aTitle to getURLandRender(aURL)

–出力対象のビューを特定
set aTargetView to (my theWebView)’s mainFrame()’s frameView()’s documentView()

–ページコンテンツがレンダリングされたサイズを取得
set targRect to aTargetView’s |bounds|()

set aData to aTargetView’s dataWithPDFInsideRect:targRect
set aImage to current application’s NSImage’s alloc()’s initWithData:aData

set bData to aImage’s TIFFRepresentation()
set aBitmap to current application’s NSBitmapImageRep’s imageRepWithData:bData

–書き出し
set myNewImageData to (aBitmap’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
set aRes to (myNewImageData’s writeToFile:bPath atomically:true) as boolean

return aRes

–Download the URL page to WebView and get page title
on getURLandRender(aURL)
  
  
set my loadDone to false
  
set my theWebView to missing value
  
openURL(aURL)
  
  
set waitLoop to 1000 * 60 –60 seconds
  
  
set hitF to false
  
repeat waitLoop times
    if my loadDone = true then
      set hitF to true
      
exit repeat
    end if
    
current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
if hitF = false then return
  
  
set jsText to “document.title”
  
set x to ((my theWebView)’s stringByEvaluatingJavaScriptFromString:jsText) as text
  
  
return x
end getURLandRender

–Down Load URL contents to WebView
on openURL(aURL)
  set noter1 to current application’s NSNotificationCenter’s defaultCenter()
  
set (my theWebView) to current application’s WebView’s alloc()’s init()
  (
my theWebView)’s setApplicationNameForUserAgent:userAgentName
  (
my theWebView)’s setMediaStyle:“screen”
  (
my theWebView)’s setDrawsBackground:true
  
–(my theWebView)’s setShouldUpdateWhileOffscreen:true
  
noter1’s addObserver:me selector:“webLoaded:” |name|:(current application’s WebViewProgressFinishedNotification) object:(my theWebView)
  (
my theWebView)’s setMainFrameURL:aURL
end openURL

–Web View’s Event Handler:load finished
on webLoaded:aNotification
  set my loadDone to true
end webLoaded:

–URL Validation Check
on validateURL(anURL as text)
  set regEx1 to current application’s NSString’s stringWithString:“((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+”
  
set predicate1 to current application’s NSPredicate’s predicateWithFormat_(“SELF MATCHES %@”, regEx1)
  
set aPredRes1 to (predicate1’s evaluateWithObject:anURL) as boolean
  
return aPredRes1
end validateURL

★Click Here to Open This Script 

2015/09/16 指定の画像をアイコン表示してdisplay dialog

外部の画像ファイルをアイコンに指定してダイアログ表示を行うAppleScriptです。

custom_icon_dialog.png

大昔から指定できることはわかっていたのですが、まったく必要がなくてやったことがなく・・・初めて指定してみました。

大きな画像を指定しても、ダイアログに表示できる大きさに自動でリサイズされます。

Animation Gifを指定するとアイコン表示エリアでアニメーションする、というのがちょっといい小ネタです。PDFも指定できますが、リンクをクリックできたりするかどうかは・・・。

AppleScript名:外部の画像をアイコン表示してdisplay dialog
set a to choose file of type {“public.image”}

display dialog “This is a simple test” with icon a

★Click Here to Open This Script 

Script Bundle内のContents/Resourcesフォルダ内に画像を入れて、アイコン名で指定するやり方もあり、こちらの方が(Xcode上で書くAppleScriptアプリケーションでも)みかけますが、通常のAppleScriptでやったことがなかったので試してみました。

resources.png

このテストのためにわざわざIconComposerでicnsリソースを作成してScriptバンドル内に入れて実験してみました。このScriptをアプリケーション形式(Applet)に保存して(同様に画像やICNSファイルを入れて)実行するとアイコン表示されます。

バンドル内のicnsファイルをアイコンとして表示できましたし、普通の画像も問題なく表示できました(Appletとして保存していれば)。

AppleScript名:バンドル内の画像を指定してdisplay dialog_app
set aPath to path to resource “bImg.icns”
display dialog “This is a simple test–1″ with icon aPath

set aPath to path to resource “aImg.png”
display dialog “This is a simple test–2″ with icon aPath

★Click Here to Open This Script 

2015/09/15 指定URLをロードして1枚ものの大きなPDFにレンダリング

Cocoaの機能を用いて、指定URLのページを読み込み、1枚ものの大きなPDFにレンダリングするAppleScriptです。

デスクトップ上に「outPdf_2.pdf」の名前で書き出します。実行には、Script Editor上でControl-Command-Rによるフォアグラウンド実行する必要があります。

まだまだ、完成にはほど遠い感じがしますが、とりあえずPDF出力できたので。

render2.png
▲本AppleScriptでレンダリングしたPDF

render1.png
▲同じページをSafariで表示させたところ

ページの上と下が「ものすごく残念な状態」になっています。一応、User AgentをSafariに合わせてみたのですが、あまり状況はよくなっていないようです。

まだまだ、本Blogを表示させると、右側のメニュー部分しか表示されなかったり、PDFの縦の長さをページ内容に合わせて自動調整できていなかったり、ページ上のグラフィックがロードされていない状態になることがあったり、、、と、前途多難な印象を受けます。

ただ、これまでこの手のオフラインレンダラーは有用な割にAppleScriptへの対応度が低く、「ならAppleScriptで自力でやっちゃえばいいじゃん」というアプローチは間違っていない(たぶん)はず。また、オフラインレンダラー自体のアップデートが割とおざなりで、OSのアップデートについてこられないことがままあります(便利なのに)。

AppleScript名:ASOCで指定URLをロードして1枚ものの大きなPDFにレンダリング
– Created 2015-09-15 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “WebKit”
use framework “Quartz”
use framework “AppKit”

property loadDone : false
property theWebView : missing value
property userAgentName : “Version/9.0 Safari/601.1.56″

set aOutPath to “~/Desktop/outPdf_2.pdf”
set aURL to “http://www.apple.com/jp/shop/browse/home/specialdeals/mac”
–set aURL to “http://piyocast.com/as”–このBlogをレンダリングすると悲惨な状態に、、、
set targX to 1024
set targY to 2000 –動的に変更できないか?

–Check If this script runs in foreground
if not (current application’s NSThread’s isMainThread()) as boolean then
  display alert “This script must be run from the main thread (Command-Control-R in Script Editor).” buttons {“Cancel”} as critical
  
error number -128
end if

set aPath to current application’s NSString’s stringWithString:aOutPath
set bPath to aPath’s stringByExpandingTildeInPath()

–URL Validation check
set aRes to validateURL(aURL)
if aRes = false then return
set aTitle to getURLandRender(aURL)
–>  ”AS Hole(AppleScriptの穴) By Piyomaru Software”
set aTargetView to (my theWebView)’s mainFrame()’s frameView()’s documentView()

set aScreen to current application’s NSScreen’s mainScreen()
set {scrX, scrY} to aScreen’s frame()’s |size|() as list

(*
set aWin to current application’s NSWindow’s alloc()’s init()
aWin’s setContentSize:{scrX, scrY}
aWin’s setContentView:(my theWebView)
aWin’s |center|()
*)

set aPrintInfo to current application’s NSPrintInfo’s sharedPrintInfo()
set newPrintOp to current application’s NSPrintOperation’s PDFOperationWithView:(aTargetView) insideRect:(current application’s NSMakeRect(0, 0, targX, targY)) toPath:bPath printInfo:aPrintInfo

set runPrint to newPrintOp’s runOperation()
return (runPrint as boolean)

–Download the URL page to WebView and get page title
on getURLandRender(aURL)
  
  
set my loadDone to false
  
set my theWebView to missing value
  
openURL(aURL)
  
  
set waitLoop to 1000 * 60 –60 seconds
  
  
set hitF to false
  
repeat waitLoop times
    if my loadDone = true then
      set hitF to true
      
exit repeat
    end if
    
current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
if hitF = false then return
  
  
set jsText to “document.title”
  
set x to ((my theWebView)’s stringByEvaluatingJavaScriptFromString:jsText) as text
  
  
return x
end getURLandRender

–Down Load URL contents to WebView
on openURL(aURL)
  set noter1 to current application’s NSNotificationCenter’s defaultCenter()
  
set (my theWebView) to current application’s WebView’s alloc()’s init()
  (
my theWebView)’s setApplicationNameForUserAgent:userAgentName
  
noter1’s addObserver:me selector:“webLoaded:” |name|:(current application’s WebViewProgressFinishedNotification) object:(my theWebView)
  (
my theWebView)’s setMainFrameURL:aURL
end openURL

–Web View’s Event Handler:load finished
on webLoaded:aNotification
  set my loadDone to true
end webLoaded:

–URL Validation Check
on validateURL(anURL as text)
  set regEx1 to current application’s NSString’s stringWithString:“((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+”
  
set predicate1 to current application’s NSPredicate’s predicateWithFormat_(“SELF MATCHES %@”, regEx1)
  
set aPredRes1 to (predicate1’s evaluateWithObject:anURL) as boolean
  
return aPredRes1
end validateURL

★Click Here to Open This Script 

2015/09/15 NSCountedSetでNSDictionaryの登場頻度集計

Cocoaの機能を用いて、recordの登場頻度集計を行うAppleScriptです。

{{aName:”Piyomaru”, aFavorite:”Sleeping”}, {aName:”Piyoko”, aFavorite:”TV”}, {aName:”Piyomaru”, aFavorite:”Sleeping”}}

というレコード(のリスト)があった場合に、この各要素、

  {aName:”Piyomaru”, aFavorite:”Sleeping”}
  {aName:”Piyoko”, aFavorite:”TV”}

のそれぞれの出現頻度のカウントを行ってみました。Pure AppleScriptのrecordだと、

  {ラベル:属性値}

のような構成で、「ラベル」の部分はテキスト(かつ、AppleScript処理系の持っている予約語、▲▲廛螢院璽轡腑鵑陵縮鷂譟↓Script Editorの予約語・・などなど(OSAXの予約語もあったかも)とかぶらないもの)である必要がありますが、NSDictionaryでは「ラベル」の部分にオブジェクトを指定できるため、

  {{aName:”Piyoko”, aFavorite:”TV”}:属性値}

のようなPure AppleScriptからすると無茶な構造のデータも持てるわけで、

strangedict.png

それならばNSCountedSetでrecordの登場頻度の集計も普通に行えるだろう、と考えてためしてみました。

昨日の段階でNSCountedSetにrecordを突っ込んで集計らしきものはできていたのですが、集計データの取り出しに失敗(おしい!)。一晩ぐっすり寝たら翌朝問題なくできました。

{​​​​​{​​​​​​​aCount:1, ​​​​​​​aData:{​​​​​​​​​aName:”Piyoko”, ​​​​​​​​​aFavorite:”TV”​​​​​​​}​​​​​}, ​​​​​{​​​​​​​aCount:2, ​​​​​​​aData:{​​​​​​​​​aName:”Piyomaru”, ​​​​​​​​​aFavorite:”Sleeping”​​​​​​​}​​​​​}​​​}

という結果を出力します。

AppleScript名:NSCountedSetでNSDictionaryの頻度集計 v2
– Created 2015-09-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aRecList to {{aName:“Piyomaru”, aFavorite:“Sleeping”}, {aName:“Piyoko”, aFavorite:“TV”}, {aName:“Piyomaru”, aFavorite:“Sleeping”}}
set aCountedList to countEachRecord(aRecList)
–>  (NSArray) {​​​​​{​​​​​​​aCount:1, ​​​​​​​aData:{​​​​​​​​​aName:”Piyoko”, ​​​​​​​​​aFavorite:”TV”​​​​​​​}​​​​​}, ​​​​​{​​​​​​​aCount:2, ​​​​​​​aData:{​​​​​​​​​aName:”Piyomaru”, ​​​​​​​​​aFavorite:”Sleeping”​​​​​​​}​​​​​}​​​}

on countEachRecord(aRecList)
  set theCountedSet to current application’s NSCountedSet’s |set|()
  
repeat with i in aRecList
    set j to contents of i
    (
theCountedSet’s addObject:j)
  end repeat
  
  
set theEnumerator to theCountedSet’s objectEnumerator()
  
set anArray to current application’s NSMutableArray’s alloc()’s init()
  
  
repeat
    set aDict to current application’s NSMutableDictionary’s alloc()’s init()
    
    
set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    
set aCount to theCountedSet’s countForObject:aValue
    
    
aDict’s setObject:aCount forKey:“aCount”
    
aDict’s setObject:aValue forKey:“aData”
    
anArray’s addObject:aDict
  end repeat
  
  
return anArray
end countEachRecord

★Click Here to Open This Script 

2015/09/14 地域、事業所名のリストを出現頻度カウントなどの処理を行って整形

家族から頼まれて作成したプログラムで、割と手こずったデータ処理のAppleScriptです。

Excelに入っているデータを読み取ってAppleScript側で集計処理を行ってみました。Pure AppleScript的な処理とCocoa的な処理がいろいろと入り混じっており、逆に効率が悪くなっているかもしれません(ただし、BridgePlusを活用しまくり、Cocoaの機能を利用して処理を書きまくっているので、処理速度は爆速)。

ここに掲載したScriptは、その処理の一部ですが・・・単体で実行可能なものです。

{「営業所エリア名」、「事業所名」}のペアになっているリストで頻度集計を行うというものですが、同時に「営業所エリア名」を「エリア名略称」に変換します。

Input:{{”西営業所”, “◯◯◯◯◯”}, {”四国営業所”, “●●●●”}, {”西営業所”, “◯◯◯◯◯”}}

Output:
(XXXX西部)◯◯◯◯◯ (2)
(XXXX四国)●●●●

非常に些細な処理ですが、実際に組んでみるとそれなりに手間がかかりました。

NSCountedSetにNSDictionaryを突っ込んで頻度カウントができるんじゃないかと思って試行錯誤してみたのですが、なかなかうまくいきませんでした(どうやら突っ込んでカウントはできているものの、値を取り出すのに失敗)。まだ、なかなか難しいです(汗)。

AppleScript名:地域、事業署名のリストを出現頻度カウントなどの処理を行って整形v2
– Created 2015-09-13 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

load framework

set oList to {{“西営業所”, “◯◯◯◯◯”}, {“四国営業所”, “●●●●”}, {“西営業所”, “◯◯◯◯◯”}}

set aRes to formatBranchDescriptions(oList)
–>
(*
“(XXXX西部)◯◯◯◯◯ (2)
(XXXX四国)●●●●

*)

on formatBranchDescriptions(oList)
  set labelList to {“branchName”, “elipName”}
  
set aList to BridgePlus’s sublistsIn:oList asDictionariesUsingLabels:labelList
  
  
—set aList to {{branchName:”西営業所”, elipName:”◯◯◯◯◯”}, {branchName:”四国営業所”, elipName:”●●●●”}, {branchName:”西営業所”, elipName:”◯◯◯◯◯”}}
  
  
set anArray to Cocoaify aList
  
  
set elipNameList to (anArray’s valueForKey:“elipName”) as list
  
–>  {”◯◯◯◯◯”, “●●●●”, “”◯◯◯◯◯”}
  
set eList to uniquify1DList(elipNameList) of me –ユニーク化
  
  
set outStr to “”
  
repeat with i in eList
    set j to contents of i
    
set aRes to countSpecifiedItem(elipNameList, {j}) of me
    
    
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“elipName == %@”, j)
    
set aRec to (anArray’s filteredArrayUsingPredicate:aPredicate)
    
set anItem to branchName of first item of aRec
    
set eName2 to convBranchToElip(anItem)
    
if aRes > 1 then
      set outStr to outStr & {eName2 & j & “ (” & aRes & “)”} & return
    else
      set outStr to outStr & {eName2 & j} & return
    end if
  end repeat
  
return outStr
end formatBranchDescriptions

–リスト中の指定項目の出現回数を返す
on countSpecifiedItem(aList, countItem)
  set aRes to BridgePlus’s indexesOfItems:countItem inList:aList inverting:false
  
set aCount to length of aRes
  
return aCount
end countSpecifiedItem

–1D Listをユニーク化
on uniquify1DList(theList as list)
  set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:“@distinctUnionOfObjects.self”
  
set bList to ASify from bArray as list
  
return bList
end uniquify1DList

–営業所エリア名、エリア名略称変換
on convBranchToElip(aBranch)
  set aList to {{branchName:“東京営業所”, elipName:“(XXXX東京)”}, {branchName:“千葉営業所”, elipName:“(XXXX千葉)”}, {branchName:“神奈川営業所”, elipName:“(XXXX神奈川)”}, {branchName:“西営業所”, elipName:“(XXXX西部)”}, {branchName:“四国営業所”, elipName:“(XXXX四国)”}, {branchName:“茨城営業所”, elipName:“(XXXX茨城)”}}
  
set aDic to Cocoaify aList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“branchName == %@”, aBranch)
  
set aRes to (aDic’s filteredArrayUsingPredicate:aPredicate)
  
try
    set anItem to contents of first item of (aRes as list)
    
set eName to (elipName of anItem)
  on error
    set eName to “”
  end try
  
return eName
end convBranchToElip

★Click Here to Open This Script 

2015/09/14 マルチバイト文字の検出、の落とし穴

Shane Stanleyから指摘のあった内容です。

マルチバイト文字(全角文字)の検出を行うAppleScriptがあくまでASCIIの0〜127のレンジを超えているかどうか、という検出を行っているだけだよ、というお話です。

実際に、ASCII Code 128〜255の文字を与えてマルチバイト文字かどうか判定させてみると、マルチバイトでもないのにマルチバイト文字として判定されます。

Objective-Cでどうやっているのかと調べてみると、日本語用のNSCharacterSetを作って使っている方もいるようで。

AppleScriptだけでもこのあたりをもっとうまく扱えるとよさそうですが・・・とりあえずは、「半角カナ」(ASCII Code 128以降)については、事前に全角カナに置換するような「前処理」を通すか、「半角カナについては処理対象外」と決めておくとか、段階的にOS上で入力できないようにしていくとか、そういう対応になっているのが現状ではないかと。

・・・というわけで、食事をしている間にナイスアイデアを。キーボードから入力可能な半角文字すべて(数字、アルファベット、記号、半角カナ)から構成されるCharacterSetをでっちあげて、これに入っていればsingle byte、入っていなければdouble byteとか(改行コードとかはどーするんだろう、というあたりはあらかじめ削除していただくとかして)。

AppleScript名:マルチバイト文字の検出の癖
– Created 2015-09-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aNum to ASCII number of “”” –Multi Byte Characterではない
–>  129
set aStr to string id 129
set aRes to my chkMultiByteChar:aStr
–>  false  –Multi Byte Characterだと判定される

–全角文字が存在するか → 7bitの文字(ASCII Code 0〜127)であるかどうかのチェックをしている
on chkMultiByteChar:checkString
  set aStr to current application’s NSString’s stringWithString:checkString
  
set aRes to aStr’s canBeConvertedToEncoding:(current application’s NSASCIIStringEncoding)
  
return (aRes as boolean)
end chkMultiByteChar:

★Click Here to Open This Script 

AppleScript名:マルチバイト文字の検出 v2
– Created 2015-09-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aRes to my chkSingleByteChar:"A"
–> true
set bRes to my chkSingleByteChar:"”"
–> true

on chkSingleByteChar:checkString
  set muCharSet to current application’s NSCharacterSet’s alphanumericCharacterSet()’s mutableCopy()
  
muCharSet’s addCharactersInString:"$\"!~&=#[]._-+~`|{}?%^*/’@-/:;(),0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\\”。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚" –Every Single-byte code which has visible characters
  
set ret to my chkCompareString:checkString baseString:muCharSet
  
return ret as boolean
end chkSingleByteChar:

–This works correctly
on chkCompareString:checkString baseString:baseString
  set aScanner to current application’s NSScanner’s localizedScannerWithString:checkString
  
aScanner’s setCharactersToBeSkipped:(missing value)
  
aScanner’s scanCharactersFromSet:baseString intoString:(missing value)
  
return (aScanner’s isAtEnd()) as boolean
end chkCompareString:baseString:

★Click Here to Open This Script 

2015/09/14 ASOCで文字種別を判定する v2

Cocoaの機能を用いて、文字種別を判定するAppleScriptです。

記号(Symbol)のチェックと、製品コードなどのルールを供給するとそのとおりの文字の並びになっているかをチェックするルーチンを加えました。

これまでに作ってきた同種のルーチンは以下のようになります。

コードのチェック
簡易マクロ展開つきのコードチェック v1

わざわざ文字を分解するよりも、ルールから正規表現の文字列を組み立てて、そのとおりになっているかのチェックを行うほうがよいかも・・・とは考えたのですが、どの桁にエラーがあるかを確認するためにはこのような仕様のほうがよいだろうか、と思うものです。

AppleScript名:ASOCで文字種別を判定する v2
– Created 2015-09-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–http://core-tech.jp/gijutsublog/2014/06/23/10505

set aRes to my chkStrings:“0123-45-678A” asRule:“9999-99-999X”
–>  {​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true​​​}
set bRes to my chkStrings:“0123-45-d2aA” asRule:“9999-99-999X”
–>  {​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​true, ​​​​​false, ​​​​​true, ​​​​​false, ​​​​​true​​​}

–指定したルールのとおりの文字種の並びになっているか?
on chkStrings:aStr asRule:aRuleStr
  set aList to characters of aStr
  
set rList to characters of aRuleStr
  
if (length of aList is not equal to length of rList) then return false
  
  
set chkList to {}
  
repeat with i from 1 to (length of aList)
    set j1 to contents of item i of aList
    
set j2 to contents of item i of rList
    
    
if j2 = “9″ then
      set j3 to (my chkNumeric:j1)
    else if j2 = “X” then
      set j3 to (my chkAlphabet:j1)
    else if j2 is in {“$”, “\”", “!”, “~”, “&”, “=”, “#”, “[”, “]”, “.”, “_”, “-”, “+”, “`”, “|”, “{”, “}”, “?”, “%”, “^”, “*”, “/”, “’”, “@”, “-”, “/”, “:”, “;”, “(”, “)”, “,”} then
      set j3 to (my chkSymbol:j1)
    end if
    
set the end of chkList to j3
  end repeat
  
return chkList
end chkStrings:asRule:

– アルファベットのみか
on chkAlphabet:checkString
  set aStr to current application’s NSString’s stringWithString:checkString
  
set allCharSet to current application’s NSMutableCharacterSet’s alloc()’s init()
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of “a”, 26))
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of “A”, 26))
  
set aBool to my chkCompareString:aStr baseString:allCharSet
  
return aBool as boolean
end chkAlphabet:

–数字のみか
on chkNumeric:checkString
  set digitCharSet to current application’s NSCharacterSet’s characterSetWithCharactersInString:“0123456789″
  
set ret to my chkCompareString:checkString baseString:digitCharSet
  
return ret as boolean
end chkNumeric:

–アルファベットと数字のみか
on chkAlphaNumeric:checkString
  set alnumCharSet to current application’s NSCharacterSet’s alphanumericCharacterSet()
  
set ret to my chkCompareString:checkString baseString:alnumCharSet
  
return ret as boolean
end chkAlphaNumeric:

–アルファベットと数字と記号のみか
on chkAlphaNumericSymbol:checkString
  set muCharSet to current application’s NSCharacterSet’s alphanumericCharacterSet()’s mutableCopy()
  
muCharSet’s addCharactersInString:“$\”!~&=#[]._-+`|{}?%^*/’@-/:;(),”
  
set ret to my chkCompareString:checkString baseString:muCharSet
  
return ret as boolean
end chkAlphaNumericSymbol:

–記号のみか
on chkSymbol:checkString
  set muCharSet to current application’s NSCharacterSet’s alloc()’s init()
  
muCharSet’s addCharactersInString:“$\”!~&=#[]._-+`|{}?%^*/’@-/:;(),”
  
set ret to my chkCompareString:checkString baseString:muCharSet
  
return ret as boolean
end chkSymbol:

–全角文字が存在するか
on chkMultiByteChar:checkString
  set aStr to current application’s NSString’s stringWithString:checkString
  
set aRes to aStr’s canBeConvertedToEncoding:(current application’s NSASCIIStringEncoding)
  
return (aRes as boolean)
end chkMultiByteChar:

on chkCompareString:checkString baseString:baseString
  set aScanner to current application’s NSScanner’s localizedScannerWithString:checkString
  
aScanner’s setCharactersToBeSkipped:(missing value)
  
aScanner’s scanCharactersFromSet:baseString intoString:(missing value)
  
return (aScanner’s isAtEnd()) as boolean
end chkCompareString:baseString:

★Click Here to Open This Script 

2015/09/13 文字種別を判定する

Cocoaの機能を用いて文字種別の判定を行うAppleScriptです。

Objective-Cのものをそのまま書き換えたレベルですが、Pure AppleScriptでやるやり方(そもそも仕組みがないから、自分で文字コードを調べてひたすらループして判定)とはずいぶん違うことがわかります。

AppleScript名:ASOCで文字種別を判定する
– Created 2015-09-12 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

–http://core-tech.jp/gijutsublog/2014/06/23/10505

set aRes to my chkAlphabet:"p"
–>  true
set bRes to my chkAlphabet:"あ"
–>  false
set cRes to my chkMultiByteChar:"a"
–>  true
set dRes to my chkMultiByteChar:"ぴ"
–>  false
set eRes to my chkAlphaNumeric:"01A"
–>  true
set fRes to my chkAlphaNumeric:"%"
–>  false
set gRes to my chkAlphaNumericSymbol:"AAA`*+}{"
–>  true
set hRes to my chkNumeric:"0123"
–> true
set hRes to my chkNumeric:"0123A"
–> false

– アルファベットのみか
on chkAlphabet:checkString
  set aStr to current application’s NSString’s stringWithString:checkString
  
set allCharSet to current application’s NSMutableCharacterSet’s alloc()’s init()
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "a", 26))
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "A", 26))
  
set aBool to my chkCompareString:aStr baseString:allCharSet
  
return aBool as boolean
end chkAlphabet:

–数字のみか
on chkNumeric:checkString
  set digitCharSet to current application’s NSCharacterSet’s characterSetWithCharactersInString:"0123456789"
  
set ret to my chkCompareString:checkString baseString:digitCharSet
  
return ret as boolean
end chkNumeric:

–アルファベットと数字のみか
on chkAlphaNumeric:checkString
  set alnumCharSet to current application’s NSCharacterSet’s alphanumericCharacterSet()
  
set ret to my chkCompareString:checkString baseString:alnumCharSet
  
return ret as boolean
end chkAlphaNumeric:

–アルファベットと数字と記号のみか
on chkAlphaNumericSymbol:checkString
  set muCharSet to current application’s NSCharacterSet’s alphanumericCharacterSet()’s mutableCopy()
  
muCharSet’s addCharactersInString:"$\"!~&=#[]._-+`|{}?%^*/’@-/:;(),"
  
set ret to my chkCompareString:checkString baseString:muCharSet
  
return ret as boolean
end chkAlphaNumericSymbol:

–全角文字が存在するか
on chkMultiByteChar:checkString
  set aStr to current application’s NSString’s stringWithString:checkString
  
set aRes to aStr’s canBeConvertedToEncoding:(current application’s NSASCIIStringEncoding)
  
return (aRes as boolean)
end chkMultiByteChar:

on chkCompareString:checkString baseString:baseString
  set aScanner to current application’s NSScanner’s localizedScannerWithString:checkString
  
aScanner’s setCharactersToBeSkipped:(missing value)
  
aScanner’s scanCharactersFromSet:baseString intoString:(missing value)
  
return (aScanner’s isAtEnd()) as boolean
end chkCompareString:baseString:

★Click Here to Open This Script 

2015/09/11 画面をロックする

Cocoaの機能を用いて・・・はいますが、Cocoaの機能を用いてshellコマンドを呼び出して画面をロックするAppleScriptです。

NSTaskでshellコマンドにオプションを指定して実行する際のわかりやすい実例でもあります。

img_3109_resized.png
▲実行すると・・・

img_3110_resized.png
▲ロックスクリーンの状態になります。再ログインするアカウントを選んでパスワードを正しく入力すると元に戻ります

AppleScript名:ASOCでLock Screen
– Created 2015-09-11 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aTask to current application’s NSTask’s alloc()’s init()
aTask’s setLaunchPath:“/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/CGSession”
aTask’s setArguments:(current application’s NSArray’s arrayWithObject:“-suspend”)
aTask’s |launch|()

★Click Here to Open This Script 

2015/09/11 与えられたテキストの言語を自動判別して対応する言語のTTS Voiceで読み上げ

Cocoaの機能を用いて、与えられたテキストの言語を自動判別して対応する言語のTTS Voiceで読み上げするAppleScriptです。Shane StanleyのAppleScript Libraryである「BridgePlus」(フリー)のインストールが必要です。

こういうのがやりたくて、地道に部品を揃えてきたわけで・・・やらないわけにはいきません。

ただし、思ったよりも泥沼に足を突っ込みかけました。食後のハイキングのはずが、雪山で遭難しかけたという印象です。

NSSpeechSynthesizerのavailableVoices()は「インストールされていないTTS Voice」も返してくるため、せっかくテキストの言語を特定して、TTS Voiceを取得しても・・・インストールされていないTTS Voiceが返ってくるとお手上げです(T_T)。

tts_voice_list.png

英語だとNSLinguisticTaggerによる判定で”en”が、フランス語だと”fr”が返ってきますが、”en”はあくまで英語を示すものであり、”en_GB”もあれば”en_US”もあれば、”en_IN”(インド)、”en-scotland”(スコットランド)、”en_ZA”(南アフリカ)もあるわけです(自分も物好きですが、南アフリカのTTS Voiceはインストールしていません)。

“en”をキーにして、全TTS Voiceから言語コードを取得し、最初にヒットした言語コードが”en_ZA”であれば南アフリカのTTS Voiceでsayコマンドの実行を行おうとしてしまうので、”en”を”en-US”に置き換えるような、つじつまあわせの処理が必要になってしまいました(もう少しうまいやり方もありそうですが、食後の散歩のレベルを超えてしまいますので)。

実際に実戦レベルのAppleScriptを書いてみるまで、見えてこない「落とし穴」もあるので、こうして細かいScriptを書いておくという作業はなかなかあなどれません、、、

AppleScript名:与えられたテキストの言語を自動判別して対応する言語のTTS Voiceで読み上げ
– Created 2015-09-11 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use BridgePlus : script “BridgePlus”

load framework

set aRes to getVoiceLanguageFromTTSVoices() –Language Code

–English, French, Japanese, Chinese
set cList to {“With the new MacBook, we set out to do the impossible.”, “Le nouveau MacBook est le résultat d’un défi presque impossible”, “新しいMacBookとともに、私たちは不可能だと思えることに挑みました。”, 于全新 MacBook,我们给自己定了一个几乎不可能实现的目}

–各言語テキストでループ
repeat with i in cList
  set j to contents of i
  
set aLang to specifyLanguageOfText(j) –テキストから言語コードを取得
  
  
–つじつまあわせ。ちょっとカッコ悪い
  
–(NSSpeechSynthesizerのavailableVoicesでインストールされていないVoiceまで返ってくることへの対策)
  
if aLang = “en” then
    set aLang to “en-US”
  else if aLang = “fr” then
    set aLang to “fr-FR”
  else if aLang = “ja” then
    set aLang to “ja-JP”
  end if
  
  
–全ボイス中から言語をサポートしているものを抽出
  
repeat with ii in aRes
    set jj to contents of ii
    
    
if (jj as text) is equal to (aLang as text) then
      
      
–最初にヒットした言語コードでPremium Voiceを検索
      
set v1fList to getPremiumVoiceNameWithFiltering(“VoiceGenderFemale”, jj)
      
set v1mList to getPremiumVoiceNameWithFiltering(“VoiceGenderMale”, jj)
      
set v1List to v1fList & v1mList
      
      
if v1List = {} then
        –Premium Voiceでヒットしなかった場合に、すべてのVoiceを検索
        
set v2fList to getAllVoiceNameWithFiltering(“VoiceGenderFemale”, jj)
        
set v2mList to getAllVoiceNameWithFiltering(“VoiceGenderMale”, jj)
        
set v2List to v2fList & v2mList
        
        
if v2List = {} then
          display dialog “この言語(” & jj & “)をサポートするTTS Voiceはインストールされていません。” –Error
          
exit repeat
        else
          copy v2List to v1List
        end if
        
      end if
      
      
repeat with iii in v1List
        set aVoice to contents of iii
        
try
          –総当たりでSayコマンド実行(インストールされていないTTS Voiceも取得できてしまうため)
          
tell current application
            say j using aVoice
          end tell
          
exit repeat
        end try
      end repeat
      
      
exit repeat
    end if
  end repeat
  
end repeat

–Premium Voices Only
on getPremiumVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLanguage == %@ and VoiceGender == %@ and VoiceIdentifier ENDSWITH[c] %@”, voiceLang, voiceGender, “premium”)
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getPremiumVoiceNameWithFiltering

–All Voices (VoiceLanguageで抽出)
on getAllVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLanguage == %@ and VoiceGender == %@”, voiceLang, voiceGender)
  
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getAllVoiceNameWithFiltering

on getLocaleIdentifierFromTTSVoices()
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLocaleIdentifier”)
  
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
end getLocaleIdentifierFromTTSVoices

on getVoiceLanguageFromTTSVoices()
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLanguage”)
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
end getVoiceLanguageFromTTSVoices

on specifyLanguageOfText(aStr)
  set aNSstring to current application’s NSString’s stringWithString:aStr
  
set tagSchemes to current application’s NSArray’s arrayWithObjects:(current application’s NSLinguisticTagSchemeLanguage)
  
set tagger to current application’s NSLinguisticTagger’s alloc()’s initWithTagSchemes:tagSchemes options:0
  
tagger’s setString:aNSstring
  
set aLanguage to tagger’s tagAtIndex:0 |scheme|:(current application’s NSLinguisticTagSchemeLanguage) tokenRange:(missing value) sentenceRange:(missing value)
  
return aLanguage as text
end specifyLanguageOfText

–1D Listをユニーク化してソート
on uniquifyAndSort1DList(theList as list, aBool as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:“@distinctUnionOfObjects.self”
  
set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“compare:”
  
set cArray to bArray’s sortedArrayUsingDescriptors:{aDdesc}
  
set bList to (ASify from cArray) as list
  
return bList
end uniquifyAndSort1DList

–文字置換
on repChar(aStr, targStr, repStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:targStr withString:repStr
  
set cString to (ASify from bString) as string
  
return cString
end repChar

★Click Here to Open This Script 

2015/09/10 テキストの言語を推測する

Cocoaの機能を用いて、与えられたテキストがどの言語のものかをNSLinguisticTaggerで求めるAppleScriptです。

資料がさほど多くないうえに、落とし穴(Blocks構文はAppleScriptにBridgeしてもらえないので、Blocks構文が必要なAPIは呼び出せない。なんとかしてほしいんですけど〜)を避けなければならないため、短い割には調査に苦労しました(プログラミングではなくて、調査なんですね、コレは)。

形態素解析ぐらいは、Blocksなしで呼び出してみたいもんです。

AppleScript名:テキストの言語を推測する
– Created 2015-09-10 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–English Text
set aStr to “With the new MacBook, we set out to do the impossible: engineer a full-size experience into the lightest and most compact Mac notebook ever. That meant reimagining every element to make it not only lighter and thinner but also better. The result is more than just a new notebook. It’s the future of the notebook.”
set aLang to specifyLanguageOfText(aStr)
–>  ”en”

–French Text
set aStr to “Le nouveau MacBook est le résultat d’un défi presque impossible : concevoir le Mac portable le plus léger et le plus compact jamais créé, sans dénaturer l’expérience Mac. Pour en arriver là, il nous a fallu repenser tous les éléments un à un, pour les rendre non seulement plus légers et plus fins, mais aussi plus perfectionnés. Le fruit de notre travail est bien plus qu’un nouvel ordinateur portable. C’est l’avenir de l’ordinateur portable.”
set aLang to specifyLanguageOfText(aStr)
–>  ”fr”

–Japanese Text
set aStr to “新しいMacBookとともに、私たちは不可能だと思えることに挑みました。これまでで最も軽く、最もコンパクトなMacのノートブックに、フルサイズの体験を与える、という目標です。それは、すべての要素をより軽く、より薄いものにするだけでなく、より優れたものにすることを意味しました。だから私たちは、すべての要素を一から考え直したのです。その結果、一台の新しいノートブック以上のものが生まれました。ノートブックの未来です。”
set aLang to specifyLanguageOfText(aStr)
–>  ”ja”

–Chinese Text
set aStr to 于全新 MacBook,我们给自己定了一个几乎不可能实现的目:在更为轻紧凑的 Mac 电脑之上,打造全尺寸的使用体就要求个元素都必重新想,不令其更为纤巧,要更加出色。最们带来的,不是一部全新的电脑,更是电脑的未来。”
set aLang to specifyLanguageOfText(aStr)
–>  ”zh-Hans”

on specifyLanguageOfText(aStr)
  set aNSstring to current application’s NSString’s stringWithString:aStr
  
set tagSchemes to current application’s NSArray’s arrayWithObjects:(current application’s NSLinguisticTagSchemeLanguage)
  
set tagger to current application’s NSLinguisticTagger’s alloc()’s initWithTagSchemes:tagSchemes options:0
  
tagger’s setString:aNSstring
  
set aLanguage to tagger’s tagAtIndex:0 |scheme|:(current application’s NSLinguisticTagSchemeLanguage) tokenRange:(missing value) sentenceRange:(missing value)
  
return aLanguage as text
end specifyLanguageOfText

★Click Here to Open This Script 

2015/09/09 指定URLのファイルをダウンロード

Cocoaの機能を用いて、指定URLのファイルをダウンロードするAppleScriptです。

書いている途中でどうしてもNSURLErrorDomain Error 3001が出てダウンロードできなかったのですが、それはダウンロード先をファイル名まで指定していなかったからでした(Appleのサンプルどおりに書いたのに!)。

最初の掲載時にはcachePolicyにNSURLRequestUseProtocolCachePolicyを指定していたのですが、ネット非接続時も正常終了したり、キャッシュが効いて最新版が取得できないなどの問題を見かけたため、NSURLRequestReloadIgnoringLocalCacheDataに変更しました。

AppleScript名:ASOCで指定URLのファイルをダウンロード
– Created 2015-09-09 Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–https://developer.apple.com/library/mac/documentation/Cocoa/
– Conceptual/URLLoadingSystem/Tasks/UsingNSURLDownload.html

property dlF : false
property dlTimeOutSec : 60.0

set my dlF to false

–DL先はファイル名まで指定しないとエラー(Error 3001)になる
set theDLdir to (current application’s NSHomeDirectory())’s stringByAppendingPathComponent:“Desktop/index.html”
–>  (NSString) “/Users/me/Desktop”

downloadFrom(“http://www.apple.com/jp/index.html”, theDLdir) of me

set waitLoop to 1000 * dlTimeOutSec –60 seconds
set hitF to false
repeat waitLoop times
  if my dlF is not equal to false then
    set hitF to true
    
exit repeat
  end if
  
current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
end repeat
if hitF = false then return
return dlF
–>  true

on downloadFrom(aURLstr, dlDir)
  
  
set aURL to current application’s |NSURL|’s URLWithString:aURLstr
  
–>  (NSURL) http://www.apple.com
  
  
set theReq to current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:dlTimeOutSec
  
–>  (NSURLRequest) { URL: http://www.apple.com }
  
  
set theDownload to current application’s NSURLDownload’s alloc()’s initWithRequest:theReq delegate:me
  
–>  (NSURLDownload) { request: { URL: http://www.apple.com } }
  
  
theDownload’s setDestination:dlDir allowOverwrite:true
  
end downloadFrom

–Download Error Event Handler
on download:aDownload didFailWithError:anError
  set anErrorStr to anError’s localizedDescription()
  
set errorReason to (anError’s userInfo())’s objectForKey:(current application’s NSURLErrorFailingURLStringErrorKey)
  
set my dlF to {anErrorStr, errorReason}
end download:didFailWithError:

–Download Finished Event Handler
on downloadDidFinish:aDownload
  set my dlF to true
end downloadDidFinish:

★Click Here to Open This Script 

2015/09/09 続・伏字文字列を作成する

ここに掲載するScriptのMACアドレスやらシリアル番号やらを伏字にするため「だけに」作ったAppleScript。1行のテキストのうちの数文字だけ伏字にするため、速度も何も重視していなかったほぼ作り捨てレベルのこのScriptに技術的な課題を見出してしまった人物がいます。

Shane Stanleyです。なぜだ〜(^ー^;;。

自分が作ったPure AppleScript版(A)、Shaneによる英数字しか考慮していないASOC版(B)、Shaneによる2バイト文字も考慮したASOC版(C)の3つのバージョンが登場。

実行速度は、

 (A):◼️◼️ 0.0001976 sec
 (B):◼️◼️◼️◼️ 0.00038 sec
 (C):◼️◼️◼️◼️◼️◼️◼️◼️◼️◼️ 0.001 sec

ぐらい違います(◼️が少ない方が速い)。

v3vsv4.png

伏字と考えると分かりにくいですが(日本語の伏字は、「伏せてもなぜか意味が分かる、ハイレベルな文芸活動」だったりするので)、「文字グループを指定した文字置換」ととらえると、技術的にツッコミを入れるべき対象に見えるかもしれません。

AppleScript名:伏字文字列を作成する v3
– Created 2015-09-09 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aStr to “This is my secret information.”
set bStr to makeUnprintableChars(aStr)
–>  ”Xxxx xx xx xxxxxx xxxxxxxxxxx.”

on makeUnprintableChars(aStr)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[A-Z0-9]” withString:“X” options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[a-z]” withString:“x” options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end makeUnprintableChars

★Click Here to Open This Script 

AppleScript名:伏字文字列を作成する v4
– Created 2015-09-09 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aStr to “これは秘密のInformationです。” –Alphabet & Numeric以外でも置換可能
set bStr to makeUnprintableChars(aStr)
–>  ”xxxxxxXxxxxxxxxxxxx。”

on makeUnprintableChars(aStr)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[\\p{Lu}\\p{Lt}\\p{Nd}]” withString:“X” options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[\\p{Ll}\\p{Lm}\\p{Lo}]” withString:“x” options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end makeUnprintableChars

★Click Here to Open This Script