Archive for the 'Application Control' Category

2017/07/21 指定のPNG画像をASCII ART化してTextEditでオープン v2

jp2aを利用して指定のPNG画像を色付きHTML形式のアスキーアート化して出力し、HTMLをRTFに変換してTextEditでオープンしてフォントを変更するAppleScriptです。

指定のJPEG画像をアスキーアート化するjp2aは、Homebrew経由でインストールしてください。

asciiart4_resized.png

asciiart3_resized.png
▲このように縦長で余白の多い画像から、自動で余白部分をトリミングして処理

アスキーアート化するにあたって、元画像の余白部分をトリミングできたほうが良好な結果が得られるため、処理対象をJPEG画像からPNG画像(背景透過)に変更し、以前に使った画像余白トリミングフレームワーク「KGPixelBoundsClipKit」を用いてトリミングしてJPEG画像に変換。

トリミングしたJPEG画像をjp2aでHTMLのアスキーアートに変換し、さらにHTMLをスタイル付きテキスト(NSAttributedString)に変換。

スタイル付きテキストをRTF(リッチテキストフォーマット)に変換してファイル出力。RTFになればTextEditで扱えるので、TextEditでオープンして本文のフォントを等幅フォント(Osaka-mono)に指定してウィンドウのサイズを変更しています。

テストする際には、KGPixelBoundsClipKitフレームワークのバイナリを~/Library/Frameworksフォルダに入れてお試しください。

–> Download Framework Binary

AppleScript名:指定のPNG画像をASCII ART化してTextEditでオープン v2
– Created 2017-07-21 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “KGPixelBoundsClipKit” –https://github.com/kgn/KGPixelBoundsClip
–http://piyocast.com/as/archives/4736

set aFile to choose file of type {“public.png”}

–PNG画像の余白をトリミング
set anImage to (current application’s NSImage’s alloc()’s initWithContentsOfFile:(POSIX path of aFile))
set bImage to anImage’s imageClippedToPixelBounds()

–トリミング結果をデスクトップにJPEG形式で保存
set aDesktopPath to (current application’s NSProcessInfo’s processInfo()’s environment()’s objectForKey:(“HOME”))’s stringByAppendingString:“/Desktop/”
set savePath to aDesktopPath’s stringByAppendingString:((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.jpg”)
set fRes to saveNSImageAtPathAsJPG(bImage, savePath, 100) of me

–ASCII ARTに変換してHTMLとして出力
set anAlias to (POSIX file (savePath as string)) as alias
set htmlRes to jpegToAsciiArt(anAlias, 120) of me

–出力されたHTMLデータを評価してスタイル付きテキストに変換
set htmlData to current application’s NSString’s stringWithString:htmlRes
set keyList to {current application’s NSDocumentTypeDocumentAttribute, current application’s NSCharacterEncodingDocumentAttribute}
set valList to {current application’s NSHTMLTextDocumentType, current application’s NSUTF8StringEncoding}
set optDict to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
set aStyledStr to current application’s NSAttributedString’s alloc()’s initWithData:(htmlData’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)) options:optDict documentAttributes:(missing value) |error|:(missing value)

–スタイル付きテキストをRTFとしてデスクトップに保存
set targFol to POSIX path of (path to desktop)
set aUUID to current application’s NSUUID’s UUID()’s UUIDString() as text
set bRes to my saveStyledTextAsRTF(aUUID, targFol, aStyledStr) –PDFで書き出す
set newPath to targFol & aUUID & “.rtf”

–保存したRTFをTextEditでオープンして等幅フォント設定して、Window横幅を変更
tell application “TextEdit”
  activate
  
open (POSIX file newPath) as alias
  
  
tell text of document 1
    set font of every attribute run to “Osaka-Mono”
  end tell
  
  
tell window 1
    set {x1, y1, x2, y2} to bounds
    
set bounds to {x1, y1, (x1 + 800), y2}
  end tell
end tell

–NSImageを指定パスにJPEG形式で保存
on saveNSImageAtPathAsJPG(anImage, outPath, qulityNum as real)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSJPEGFileType) |properties|:{NSImageCompressionFactor:qulityNum})
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –true/false
end saveNSImageAtPathAsJPG

–与えられたファイルパスのJPEG画像をASCII ARTに変換して返す
on jpegToAsciiArt(anImageAlias, digitNum as integer)
  set sText to “/usr/local/bin/jp2a -f –html-raw –colors –width=” & (digitNum as string) & ” -i “ & quoted form of POSIX path of anImageAlias
  
set tRes to do shell script sText
  
return tRes
end jpegToAsciiArt

–スタイル付きテキストを指定フォルダ(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

★Click Here to Open This Script 

2017/07/09 指定URLにリンクされているPDFをすべて指定のフォルダにダウンロードする

指定URLのHTMLにリンクされているPDFをすべて指定のフォルダにダウンロードするAppleScriptです。

従来、この手の処理はSafariに対してdo javascript命令を実行していましたが、呼び出しにそこそこ時間がかかります。

そこで、SafariまかせにせずにAppleScript側でオープンソースのフレームワーク「HTMLReader」を呼び出してHTMLを解析したところ、圧倒的に高速になりました。

同じページ内のリンク箇所の抽出処理だと、

  Safari+do javascript:89 seconds
  HTMLReader.framework:0.064 seconds (First Run Time:0.831 seconds)

と、Safariに対してdo javascriptコマンドを実行しないAppleScriptのほうが100〜1,400倍高速に処理できています。本Script全体の処理時間については、PDFのダウンロード処理をともなうためネットワークの速さに依存しますが、1分かからない程度で終わることでしょう。Safari+do javascriptだとリンク先の抽出がまだ終わっていないぐらいの時間です。

実行にあたっては、HTMLReaderのプロジェクトをダウンロードしてXcode上でビルドし、出来上がったフレームワークのバイナリを~/Library/Frameworksにインストールしておく必要があります。

ただ、PDFのURLが厳密にわかっている連番のファイルならshellのcurlコマンドを呼び出してダウンロードさせれば1行で終わってしまう内容ではあります。

AppleScript名:指定URLにリンクされているPDFをすべて指定のフォルダにダウンロードする
– Created 2017-07-09 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “HTMLReader” –https://github.com/nolanw/HTMLReader
–http://piyocast.com/as/archives/4720

set aTargFol to POSIX path of (choose folder with prompt “PDFダウンロード先のフォルダを選択”)
set aTargPath to current application’s NSString’s stringWithString:aTargFol

set aStr to “http://yakumo-tajimi.com/dl.html” –Safariの最前面のウィンドウからとってきてもよい
set aList to getWebLinkURLs(aStr, “pdf”) of me

repeat with i in aList
  set j to contents of i
  
set jURL to (current application’s |NSURL|’s URLWithString:j)
  
set {exRes, headerRes, aData} to checkURLResourceExistence(jURL, 10) of me
  
  
if exRes = true then
    set cURL to (current application’s |NSURL|’s URLWithString:j)
    
set cFileName to (cURL’s |lastPathComponent|()) as string
    
set savePath to (aTargPath’s stringByAppendingPathComponent:cFileName)
    
set wRes to (aData’s writeToFile:savePath atomically:true)
  end if
end repeat

–指定のURLのページのHTMLソースからリンクを抽出して、指定拡張子に合うものだけをフルパスのURL化して返す
on getWebLinkURLs(anURLstr, linkFileExt)
  –URLの妥当性チェック(存在チェック)
  
set aURL to (current application’s |NSURL|’s URLWithString:anURLstr)
  
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
  
if exRes = false then error “Illegal URL Error” –エラー発生時に処理打ち切り
  
  
–HTMLのソースを取得する
  
set conType to headerRes’s valueForKeyPath:“Content-Type”
  
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType
  
  
–リンク箇所を抽出
  
set aTextArray to ((aHTML’s nodesMatchingSelector:“a”)’s textContent) as list –リンク文字
  
set aLinkArray to ((aHTML’s nodesMatchingSelector:“a”)’s attributes’s valueForKeyPath:“href”) as list –URL
  
  
  
–取得したリンクを拡張子で絞り込みつつ、それぞれフルパスのURLを組み立てる
  
set urlList to {}
  
set aaURL to aURL’s URLByDeletingLastPathComponent()
  
  
repeat with i in aLinkArray
    set bURL to (current application’s |NSURL|’s URLWithString:i)
    
set aRes to (bURL’s |scheme|()) as string
    
set aExt to (bURL’s |pathExtension|()) as string
    
    
if aRes = “missing value” and aExt = linkFileExt then
      –想定URL(指定サイト内)のファイルへのリンクの処理
      
set aaaURL to (aaURL’s URLByAppendingPathComponent:i)
      
set aaaURLstr to (aaaURL’s absoluteString()) as string
      
set the end of urlList to aaaURLstr
    else if aRes is not “missing value” and aExt = linkFileExt then
      –指定外のURL(想定サイト外のファイルへのリンクなど)の処理
      
set the end of urlList to (aaURL’s absoluteString()) as string
    end if
  end repeat
  
  
–重複部分を除去してユニークなリストにして返す
  
return uniquify1DList(urlList, true) of me
end getWebLinkURLs

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

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

★Click Here to Open This Script 

2017/07/07 Keynoteの現ページと次ページで場所が被っていても始点座標が異なるオブジェクトの始点座標をそろえる

編集中のKeynote書類で現在のスライドと次のスライド上の指定した種類のオブジェクトの座標情報をスキャンして、「重なっているが原点座標が異なる」ものを検出して原点座標を同じにそろえるAppleScriptです。

2017-07-07-13_06_20.gif

Keynoteで資料を使っていて、(複数ページ間で同じ場所に存在すべき)オブジェクトの位置が微妙にズレていることが気になることがあります。

ただ、いちいち座標情報をひろって手で修正するのは面倒なので、AppleScriptで自動修正させてみました。

重なっているオブジェクト同士であっても、始点座標が同じものは無視します(修正する必要はないので)。

Script実行時にチェック対象の2ページのうち、最初のページを表示していることを想定しています。

オブジェクトの領域の重なり判定は2つだけを想定しています。それ以上の個数のオブジェクトの重なりは無視しています。

KeynoteのAppleScript用語辞書がselection(選択中のオブジェクト)を取得できないという「残念な点」があるので、そこが重ね重ねも残念です。

AppleScript名:Keynoteの現ページと次ページで場所が被っていても始点座標が異なるオブジェクトの始点座標をそろえる
– Created 2017-07-07 by Takaaki Naganoya
– 2017 Piyomaru Software
– v1:オーバーラップしているオブジェクトの個数が2個のみの状況を想定
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4718

tell application “Keynote”
  tell front document
    set sNum to slide number of (current slide)
  end tell
end tell

–表示中のページ(slide)と、その次のページ(slide)で、
–指定オブジェクト(shape)の座標情報をリストアップ
set {rect1, obj1} to getObjInfoList(sNum, “shape”) of me –現在のページ(slide)
set {rect2, obj2} to getObjInfoList(sNum + 1, “shape”) of me –現在+1ページ

set hitRect to {} –衝突していつつもイコールではないオブジェクトの座標情報を入れる
set hitObj to {} –衝突しているオブジェクト(最初のページ、次のページ)をペアで入れる

set p1Count to 1

–表示中のページ(slide)と、その次のページ(slide)で、
–重なっていつつも始点座標が異なるオブジェクト(shape)を総当たりでリストアップ
tell application “Keynote”
  tell front document
    repeat with i in rect1
      set j to contents of i
      
      
set p2Count to 1
      
      
repeat with ii in rect2
        set jj to contents of ii
        
        
–指定オブジェクト同士の領域が重なっているかどうかチェック
        
set aRes to detectRectanglesCollision(j, jj) of me
        
–始点座標がイコールかどうかチェック(イコールのものは修正不要)
        
set origRes to (origin of j) = (origin of jj)
        
        
if (aRes = true) and (origRes = false) then
          set the end of hitRect to j
          
set the end of hitObj to {contents of item p1Count of obj1, contents of item p2Count of obj2}
        end if
        
        
set p2Count to p2Count + 1
        
      end repeat
      
      
set p1Count to p1Count + 1
    end repeat
  end tell
end tell

–current slideを2ページ目に変更
set cRes to changeCurrentSlide(sNum + 1) of me

–オーバーラップしていつつも始点座標が異なるアイテムの個数を数える
set tCount to count every item of hitRect

tell application “Keynote”
  tell front document
    tell current slide
      repeat with i from 1 to tCount
        set curPos to contents of item i of hitRect
        
set curOrig to origin of curPos
        
set curOrigX to x of curOrig
        
set curOrigY to y of curOrig
        
        
set curObj to contents of second item of item i of hitObj
        
set position of curObj to {curOrigX, curOrigY}
      end repeat
    end tell
  end tell
end tell

–最前面のKeynote書類の指定スライド(page)上の指定オブジェクトの情報(座標情報、オブジェクト情報)を返す
on getObjInfoList(aPage, objClassStr as string)
  set rectList to {}
  
set objList to {}
  
  
set cRes to changeCurrentSlide(aPage) of me
  
if cRes = false then error “Slide Range Error”
  
  
tell application “Keynote”
    tell front document
      tell current slide
        set aList to every iWork item
        
        
repeat with i in aList
          set aClass to (class of i) as string
          
          
if aClass = objClassStr then
            set aWidth to width of i
            
set aHeight to height of i
            
set {aPosX, aPosY} to position of i
            
set aRect to {origin:{x:aPosX, y:aPosY}, |size|:{|width|:aWidth, |height|:aHeight}}
            
            
set the end of rectList to aRect
            
set the end of objList to i
          end if
        end repeat
      end tell
    end tell
  end tell
  
  
return {rectList, objList}
end getObjInfoList

–NSRect同士の衝突判定
on detectRectanglesCollision(aRect, bRect)
  set a1Res to current application’s NSIntersectionRect(aRect, bRect)
  
return not (a1Res = {origin:{x:0.0, y:0.0}, |size|:{width:0.0, height:0.0}})
end detectRectanglesCollision

–表示中のスライド(ページ)を変更する
on changeCurrentSlide(targPageNum as integer)
  try
    tell application “Keynote”
      tell front document
        set sCount to count every slide
        
if targPageNum < 1 then return false –under check
        
if sCount < targPageNum then return false –over check
        
set current slide to slide targPageNum
        
return true
      end tell
    end tell
  on error
    return false
  end try
end changeCurrentSlide

★Click Here to Open This Script 

2017/07/05 Numbersでアクセス解析データを合計 v3

Numbersで表示している最前面の書類のすべてのシート上にある表のデータを集計するAppleScriptです。

4年分ぐらいのBlogのアクセス集計を行うことになり、ページ単位のアクセス情報をNumbers上にペースト。

numbers1.png

月ごとにシートを分けて保存し、「アクセスURL」「アクセス回数」のデータを分析することにしました。データは月ごとにシートに分けているけれども、分析を行う際にはすべてをまとめる必要がある、というのと、Numbersにそんな便利な機能はないというのがミソ。こういう用途こそAppleScriptで自動化すべきものです。

アクセス解析ページのデータをNumbersにコピペで貼り付けて、集計自体はAppleScriptで実行。

Numbersからデータを取得する部分は割とさくっとできたのですが、問題は集計部分。

Cocoaの機能を使わないと処理が大変なことはわかっていたので、とりあえずNSCountedSetにページのURLを突っ込んで集計を試みましたが、NSCountedSetを他のデータから作るのは割と融通がききませんでした。

“A”が10回存在するというデータをNSCountedSetに突っ込もうとすると、

  {”A”, “A”, “A”, “A”, “A”, “A”, “A”, “A”, “A”, “A”}

という配列を作って追加しないといけないようだったので、1,000回だったら1,000個のデータを含む配列を作って……とやっていたら、要素数が100万個ぐらいの巨大な配列を作ることになり、処理時間が余計にかかってしまいました(逆に、メモリ8Gバイトのマシンでよく動いたものだと)。

結局、このバージョンのように集計時には1つの巨大なレコードにページのURLとアクセス回数を記録するように変更しました。

   {”/path/to/url1″:1024, “/path/to/url2″:311…….}

このあたり、AppleScriptのレコードではURLそのものをラベルにすることは(許容されない特殊文字を含んでいるため)不可能ですが、CocoaのNSDictionary(正確にはNSMutableDictionary)であればこのようなラベルを持てるので、便利に使っています。

集計後にURLと回数を個別に取り出して配列に入れ、回数で降順ソート。

  {{accessCount:300, aURL:”/path/to/url1″}, {accessCount:200, aURL:”/path/to/url2″}}

NSCountedSetで集計していたときには80秒ぐらいかかっていましたが、NSDictionaryで集計したら7秒程度(MacBook Pro Retina 2012)で終了しました。

集計元のNumbers書類(34シート、各シートに表1つ。表の行数はだいたい1,000行ぐらい)から集計対象データを取り出す部分は4秒ぐらいなので、集計部分は3秒程度かかっています。

このAppleScript自体はそれほど汎用性があるわけではありませんが、Numbersに入れたデータをAppleScriptから集計するのも、Cocoaの機能を利用すれば割と問題なく行えるという「見本」であります。

AppleScript名:Numbersでアクセス解析データを合計 v3
– Created 2017-07-04 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4716

script spd
  property allData : {}
end script

–初期化
set (allData of spd) to {}

–Numbers書類上の各シートの表の所定の範囲からデータを取り出す
tell application “Numbers”
  set dCount to count every document
  
if dCount = 0 then return
  
  
tell front document
    set sCount to count every sheet
    
repeat with i from 1 to sCount
      tell sheet i
        tell table 1
          set aRowCount to row count
          
set aRange to range (“A2:B” & (aRowCount as string))
          
set aDat to value of every cell of aRange
          
set (allData of spd) to (allData of spd) & aDat
        end tell
      end tell
    end repeat
  end tell
end tell

–取り出したデータを集計(1つの巨大なRecordに動的に項目を作成しつつ集計)
set aLen to length of (allData of spd)
set aDic to current application’s NSMutableDictionary’s alloc()’s init()

repeat with i from 1 to aLen by 2
  set aKey to contents of item i of (allData of spd)
  
set newVal to contents of item (i + 1) of (allData of spd)
  
  
—解析先ディレクトリをしぼりこみ
  
if aKey begins with “/as/archives/” then
    set aValue to (aDic’s valueForKey:aKey)
    
if aValue = missing value then
      (aDic’s setObject:newVal forKey:aKey) –新規エントリ作成
    else
      (aDic’s setObject:(newVal + aValue) forKey:aKey) –加算
    end if
  end if
end repeat

–集計したデータを個別要素に分離し、アクセス回数でソート
set anArray to current application’s NSMutableArray’s alloc()’s init()
set allKeys to (aDic’s allKeys()) as list

repeat with i in allKeys
  set bVal to (aDic’s valueForKey:i)
  
set bDic to {accessCount:(bVal as integer), aURL:i}
  (
anArray’s addObject:bDic)
end repeat

set bList to sortRecListByLabel(anArray, “accessCount”, false) of me –降順ソート
return bList as list

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList, aLabelStr as string, ascendF as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set sortDesc to current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF
  
set sortDescArray to current application’s NSArray’s arrayWithObjects:sortDesc
  
set sortedArray to aArray’s sortedArrayUsingDescriptors:sortDescArray
  
  
return sortedArray
end sortRecListByLabel

★Click Here to Open This Script 

2017/07/04 Mac App Storeでアプリケーション「Double PDF」を販売開始

Mac App StoreでMac用アプリケーション「Double PDF」(1,080円)の販売を開始しました。すべてAppleScriptで書いたアプリケーションです。

dpdf1.png

文字主体のPDF(電子ブックなど)やKeynoteなどのオフィス系アプリケーションで作成したPDFの差分チェック用ツールです。2つのPDFを同時にめくってチェックを行うのが基本的な機能で、差分を検出するために便利な機能が備わっています。

さて、なぜこのようなアプリケーションを作ったかという経緯についてご説明を。

同じデータ(Pages書類とかMarkdown書類とか)からPDFを作成するといっても、OSやアプリケーションのバージョンが変わると微妙に生成されるPDFは変わってきます(本当)。文字詰め、行間スペース、レイアウト順など、こまかいところは実に頻繁に変わります。

また、元データ自体に修正することがあるので、きちんと反映されているのか、修正点がよそに反映しないかどうかもPDF出力して、オリジナルと比較して確認したいところです。

こういう用途で真っ先に試してみるのは、Adobe Acrobat。PDFバージョン比較機能があるので試してみたところ、500ページぐらいのPDF本に対しては「微細な変化を検出しすぎる」こともあって、実務ではとても使えませんでした。とくに、不可視データの変化を検出しまくるので、Adobe Acrobatをこの用途に使うのはほぼ無理という判断に。

そこで、前のバージョンのOSで生成した本と現在のOS上で生成した本で変化が生じていないかをチェックするAppleScriptを書き、GUIまで作ってひととおり機能評価。SSDのマシンで動かした感じでは(MacBook Pro、MacBook Air)かなりいい印象でした。

dpdf0.png
▲開発最初期バージョンの画面。ここから周囲のみなさまのダメ出しによりいろいろ試行錯誤を

PDFを画像として評価して差分をチェックするのはGPUImage.frameworkで行なっています。GPUの強力な機能がAppleScriptからでも手軽に呼び出せています。

PDF本文テキストの差分を表示するのは、外部アプリケーション「BBEdit」「TextWrangler」。Mac App Storeに提出する際のチェックで引っかかって外しましたが、初期版ではFileMergeやVimdiffなどを操作してテキストの差分を表示する機能もありました(削ってしまいましたが)。

2text_diff_by_external_app_j_resized.png
▲外部アプリケーション(BBEdit)による文字差分のブラウズ

2text_diff_by_external_app_2_resized.png
▲途中のバージョンにあった、Vimdiffによる文字差分のブラウズ(廃止)

Adobe Acrobatよりも軽快に動作し、差分チェックを「ソートした本文文字」(PDF出力時にレイアウト順がよく変わるので)、「本文文字そのまま」「プレビュー画像としてチェック」などの方式をとりまぜてさまざまなケースに対応できるようにしてみました。

よろしくお買い求めください!(告知が続いたのはたまたま、、、)

2017/06/29 Keynote Control 2冊同時刊行

Piyomaru Softwareによる電子書籍、AppleScriptえほんシリーズの続刊を2冊同時に販売開始しました(お試し版もご用意しました)。

いきなり難しくならない「ホップ」「ステップ」「ジャンプ」の3段階難易度設定。
difficulty_resized.png

どこからどこまでが1行かを明示的に示す行番号表示などで、わかりやすく説明しています。

linenumbers.png

「Keynote Control 1」(47ページ)と「Keynote Control 2」(45ページ)です。BOOTH.pmにて委託販売しており、BOOTH上での価格は両方とも1,000円です。

同時に、既刊の「iTunes Control」についてもBOOTH上での販売価格を1,000円に改定しています。

ぜひお買い求めください。

keynote_control1_cover_resized.png

keynote_control2_cover_resized.png

2017/06/21 日の出、日没時刻を計算するv2

オープンソースのプロジェクト「EDSunriseSet」(By Ernesto García)をFramework化した「EDSunriseSet.framework」を呼び出して日の出、日没時刻を計算するAppleScriptのアップデート版です。

本AppleScriptのテストのためには、EDSunriseSet.frameworkを~/Library/Frameworksフォルダに入れておく必要があります。バイナリを用意しておきましたので、自己責任でおためしください。

→ Download Binary (26KB)

EDSunriseSetが用意している各種の日の出、日没情報を取得するようにしてみました。

日没や日の出前後の「薄明」の情報についても取得します。

1024px-twilight_subcategoriessvg_resized.png

ただ、白夜などの現象が発生しない地域でこれらの薄明の情報はとくに必要はないように感じます(個人の見解です)。

AppleScript名:日の出、日没時刻を計算するv2
– Created 2017-06-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “EDSunriseSet” –erndev/EDSunriseSet
–https://github.com/erndev/EDSunriseSet
–http://piyocast.com/as/archives/4699

set cityRecList to {{kCityName:“Tokyo”, kCityLatitude:(35.6894875), kCityLongitude:(139.6917064), kCityTimeZone:“Asia/Tokyo”}}

set dList to {}

repeat with i in cityRecList
  set the end of dList to getSunriseSunset(i) of me
end repeat
return dList
–>  {{sunrise:date “2017年6月21日水曜日 4:25:37″, sunset:date “2017年6月21日水曜日 19:00:21″, civilTwilightStart:date “2017年6月21日水曜日 3:55:35″, civilTwilightEnd:date “2017年6月21日水曜日 19:30:23″, nauticalTwilightStart:date “2017年6月21日水曜日 3:18:15″, nauticalTwilightEnd:date “2017年6月21日水曜日 20:07:44″, astronomicalTwilightStart:date “2017年6月21日水曜日 2:36:45″, astronomicalTwilightEnd:date “2017年6月21日水曜日 20:49:13″, cityname:”Tokyo”}}

on getSunriseSunset(cityRec)
  set curLocale to current application’s NSLocale’s currentLocale()
  
set curDate to current application’s NSDate’s |date|()
  
  
set aTZ to current application’s NSTimeZone’s alloc()’s initWithName:(kCityName of cityRec)
  
set aSunrizeSunset to current application’s EDSunriseSet’s alloc()’s initWithDate:curDate timezone:aTZ latitude:(kCityLatitude of cityRec) longitude:(kCityLongitude of cityRec)
  
  
–日の出、日没  
  
set aSunRiseDate to (aSunrizeSunset’s sunrise) as date
  
set aSunSetDate to (aSunrizeSunset’s sunset) as date
  
  
–https://en.wikipedia.org/wiki/Twilight
  
–https://ja.wikipedia.org/wiki/薄明
  
  
–市民薄明(常用薄明、第三薄明)
  
set aCivilTwilightStart to (aSunrizeSunset’s civilTwilightStart) as date
  
set aCivilTwilightEnd to (aSunrizeSunset’s civilTwilightEnd) as date
  
  
–航海薄明(第二薄明)
  
set aNauticalTwilightStart to (aSunrizeSunset’s nauticalTwilightStart) as date
  
set aNauticalTwilightEnd to (aSunrizeSunset’s nauticalTwilightEnd) as date
  
  
–天文薄明(第一薄明)
  
set anAstronomicalTwilightStart to (aSunrizeSunset’s astronomicalTwilightStart) as date
  
set anAstronomicalTwilightEnd to (aSunrizeSunset’s astronomicalTwilightEnd) as date
  
  
return {sunrise:aSunRiseDate, sunset:aSunSetDate, civilTwilightStart:aCivilTwilightStart, civilTwilightEnd:aCivilTwilightEnd, nauticalTwilightStart:aNauticalTwilightStart, nauticalTwilightEnd:aNauticalTwilightEnd, astronomicalTwilightStart:anAstronomicalTwilightStart, astronomicalTwilightEnd:anAstronomicalTwilightEnd, cityname:kCityName of cityRec}
end getSunriseSunset

★Click Here to Open This Script 

2017/06/19 AppleScript最新リファレンス OS X 10.11対応 P-289リスト訂正

電子書籍「AppleScript最新リファレンス OS X 10.11対応」のP-289に掲載しているプログラムリストに間違いを見つけたので訂正します。

このページ数はBOOTHにて委託販売しているPDF版のものであり、その他のブックストアで販売しているePub版については変更になっている場合があります。

「ノウハウ集」の、
book1_cor1_resized.png

「理解しやすい(読みやすい)AppleScriptの書き方」の箇所で、
book1_cor2_resized.png

OS X標準でインストールされているサンプルAppleScriptの間違いを書き換える内容を提示していますが、
book1_cor3_resized.png

ここで不等号の向きが逆になっているのを見つけたので訂正します。

AppleScript名:P-289リスト修正(修正前)
if (currentHour 12 and currentHour < 24) then
  set amPM to “PM”
else
  set amPM to “AM”
end if

★Click Here to Open This Script 

AppleScript名:P-289リスト修正(修正後)
if (currentHour 12 and currentHour < 24) then
  set amPM to “PM”
else
  set amPM to “AM”
end if

★Click Here to Open This Script 

2017/06/16 指定PDFの最初のページに大量のスクウェアアノテーションを添付する

指定PDFの最初のページに大量のスクウェアアノテーションを添付するAppleScriptです。

他のGUIアプリケーションを併用せずQuartz Frameworkの機能を利用して、PDFに対するアノテーションの添付を行います。

square_anno1.png

PDFのアノテーションまわりはmacOS 10.13で大幅に変更されているため、本Scriptがそのまま10.13上でも動作することは期待していません。

AppleScript名:指定PDFの最初のページに大量のスクウェアアノテーションを添付する
– Created 2017-06-16 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Quartz”
use framework “AppKit”
–http://piyocast.com/as/archives/4688

set aHFSPath to (choose file of type {“com.adobe.pdf”} with prompt “Select PDF”)
set aPOSIX to POSIX path of aHFSPath
set aURL to (current application’s |NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()
set aPage to aPDFdoc’s pageAtIndex:0

set firstPage to (aPDFdoc’s pageAtIndex:0)

–Remove Annotation
my removeAnnotationFromPage:firstPage –Call by Reference

–Get PDF size by Point
set aBounds to aPage’s boundsForBox:(current application’s kPDFDisplayBoxMediaBox)
set aSize to |size| of aBounds

–Add Annotation
repeat with xNum from 30 to ((width of aSize) - 30) by 50
  repeat with yNum from 30 to ((height of aSize) - 30) by 50
    set squAnn to (current application’s PDFAnnotationSquare’s alloc()’s initWithBounds:{origin:{x:xNum, y:yNum}, |size|:{width:40, height:40}})
    (
squAnn’s setValue:(current application’s NSColor’s blueColor()) forAnnotationKey:(current application’s kPDFAnnotationKey_Color))
    (
squAnn’s setValue:(current application’s NSColor’s clearColor()) forAnnotationKey:(current application’s kPDFAnnotationKey_InteriorColor))
    (
firstPage’s addAnnotation:squAnn)
  end repeat
end repeat

–Save It
aPDFdoc’s writeToFile:aPOSIX

–Remove All Annotation from a Page. Call by Reference
on removeAnnotationFromPage:aPage
  set anoList to (aPage’s annotations()) as list
  
repeat with i in anoList
    (aPage’s removeAnnotation:i)
  end repeat
end removeAnnotationFromPage:

★Click Here to Open This Script 

2017/06/09 指定PDFの最初のページからアノテーションを削除する

指定PDFの最初のページに添付されたアノテーション(Preview.app上ではマークアップと呼ばれる)を削除するAppleScriptです。

■実行前(Before)
pdf_annotation1_resized.png

■実行後(After)
pdf_annotation2_resized.png

とりあえず、指定PDFの指定ページ上のアノテーションを取得して削除できるようになりました。このあたり、もはやプログラミングではなく単なる調査です(汗)。

アノテーションを検出するScriptにも記載してあるとおり、Skimで添付したアノテーションは処理できません。Preview.appで添付したアノテーションを処理対象にしています。Preview.appで添付したアノテーションはSkimでもAdobe Acrobatでも表示が可能です。

AppleScript名:指定PDFの最初のページからアノテーションを削除する
– Created 2017-06-09 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Quartz”
–http://piyocast.com/as/archives/4681

set aHFSPath to (choose file of type {“com.adobe.pdf”} with prompt “Choose a PDF with Annotation”)
set aPOSIX to POSIX path of aHFSPath
set aURL to (current application’s |NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()

set firstPage to (aPDFdoc’s pageAtIndex:0)

set anoList to (firstPage’s annotations()) as list

repeat with i in anoList
  (firstPage’s removeAnnotation:i)
end repeat

aPDFdoc’s writeToFile:aPOSIX

★Click Here to Open This Script 

2017/06/08 指定PDFの最初のページからアノテーションを取得する

指定PDFの最初のページに添付されたアノテーション(Preview.app上ではマークアップと呼ばれる)を取得するAppleScriptです。

pdf_annotation1_resized.png

とりあえず、指定PDFの指定ページ上のアノテーションを取得して種類や大きさを取得できるようになりました。

日常的に利用しているPDFビューワーとしてはオープンソースのSkimがあり、むしろPreview.appよりもこちらの方を主に利用していますが、Skimで添付したアノテーションについては保存形式が異なる(外部保存?)ようで、本Scriptでは検知できませんでした。テストにはPreview.app上で編集して任意のアノテーション(マークアップ)を追加したPDFを用意する必要があります。

PDF上の指定ページ上のアノテーションを取得することはできるようになりましたが、取得することが目的ではなく、Script側からアノテーションを作成してPDFに添付することが最終目的です。アノテーションの作成についてはあまり情報が見つからず、ちょっと苦労させられています。

他のアプリケーションに依存しないでPDFの各種処理が行えることが望ましく(とくに、Adobe Acrobatが入っていない環境でも処理できることが望ましい)、アノテーションの添付はAppleScriptでCocoaの機能を利用して行うPDF処理としては「最後の難関」として残っています。ほかはひととおり他のアプリケーションなしでできています。

AppleScript名:指定PDFの最初のページからアノテーションを取得する
– Created 2017-06-08 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Quartz”
–http://piyocast.com/as/archives/4679

set aHFSPath to (choose file of type {“com.adobe.pdf”} with prompt “Choose a PDF with Annotation”)
set aPOSIX to POSIX path of aHFSPath
set aURL to (current application’s |NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()

set firstPage to (aPDFdoc’s pageAtIndex:0)
–>  (PDFPage) PDFPage, label 1

set anoList to (firstPage’s annotations()) as list
(*
{(PDFAnnotationMarkup) Type: ’Highlight’, Bounds: (81, 624) [434, 53]
, (PDFAnnotationSquare) Type: ’Square’, Bounds: (50, 419) [212, 162]
, (PDFAnnotationSquare) Type: ’Square’, Bounds: (301, 107) [244, 484]
}
*)

repeat with i in anoList
  set aBounds to i’s |bounds|()
  
  
log aBounds
  
(* {origin:{x:80.79, y:624.4106}, size:{width:433.6944, height:52.8918}} *)
  
(* {origin:{x:50.05553, y:419.1671}, size:{width:212.27807, height:162.3308}} *)
  
(* {origin:{x:300.6213, y:106.8405}, size:{width:244.0961, height:484.4566}} *)
  
end repeat

★Click Here to Open This Script 

2017/06/06 macOS 10.13 High Sierra

WWDC 2017のKeynoteにおいて、macOS 10.13「High Sierra」が発表になりました。

Apple File Systemの標準採用、Metal 2、より強化されたSiriなどが搭載されるようです。見た目や使い勝手が大幅に刷新されるということはないだろう、とその小幅な変更が施された名前からも期待できます。

新しい機能が搭載されるということは、例によってAppleがロクにテストも検証も修正もしないで新機能を出荷するということであり、とくにファイルシステム周りの変更は相当の混乱を生み出すことでしょう。しかも、10.13で発生したバグを10.13.xで解決しないまま10.14に移行するのはいつものことです。

それでも、ここ数年の停滞を払拭させるような試みが行われていることについては、評価に値するものと思っています。

AppleScriptが箱庭の世界の中だけでなく、Cocoa Frameworkを呼び出せるようになったために、OSの機能アップをタイムラグなしで享受できるようになりました。たとえば、プレビュー説明の文章を読んだだけでも日本語のNSSpellChecker辞書がOSレベルで提供されるようになることがうかがわれますし、強化されたGPUとGPUの機能を活かした(BNNSよりも整備された)機械学習フレームワークが提供されることも期待できます。

→ Core MLという形で提供されるようで。ただ、ドキュメント読むと学習用じゃなくて、学習ずみモデルを利用することに重点が置かれているもよう

まずは、実際に見て触って試してみないとなんとも言えません。

2017/06/05 Webサービスを用いてIPアドレスから位置情報を検索

Web APIを用いて与えられたIPアドレスから対応する位置情報を検索するAppleScriptです。

IPアドレスから位置情報を提供するWeb APIは割とたくさんあって、無料で使えるものが多いものの、サービスの提供が長続きしなかったり、サービス状態が安定しなかったり(時間帯によってアクセスできなかったり)、無料がゆえに呼び出し頻度の制約が割ときつかったりします(仕事で継続的に利用する場合には各サービスの有償コースの利用をおすすめします)。

サービス名 アクセス制限 サービス運営者の所在国
ipinfo.io 1日あたり1000リクエスト 記載なし(イギリス?)
ip-api.com 1分あたり240リクエスト ルーマニア
ipinfodb.com 1秒間に2リクエスト マレーシア

それでも、サービスを利用できると助かるケースが多いため、実際に呼び出し方法について確認しておくとよいでしょう。

AppleScript名:ipinfo.ioでIPアドレスから場所を検索
– Created 2017-06-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4675

set myIP to getGeoLocationByIPinfo(“45.59.69.202″) of me
–>  {region:”Delaware”, city:”Wilmington”, country:”US”, org:”AS3800 Talent House, Inc.”, hostname:”No Hostname”, postal:”19801″, ip:”45.59.69.202″, loc:”39.7157,-75.5281″}

–http://ipinfo.io/developers
on getGeoLocationByIPinfo(myIP)
  set aURL to “http://ipinfo.io/” & myIP
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
set aRESTres to (json of aRes)
  
set aRESTcode to responseCode of aRes
  
if aRESTcode is not equal to 200 then return false
  
return aRESTres as record
end getGeoLocationByIPinfo

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

2017/05/29 Search a keyword from Wikipedia and get category tags v1e

This AppleScript search a keyword from Wikipedia and get category tags.

We can get keyword meaning tags from Web APIs such as Microsoft Cognitive services.

But there is a quick and dirty way to get the keyword tags by searching a keyword and get category information from Wikipedia.

wiki_e0_resized.png

wiki_e1_resized.png

This AppleScript does not consider synonyms. There is a clear difference between Japanese version Wikipedia and English version Wikipedia.

Japanese language is made from very few sound element. So, it has a lot of synonyms.

Bridge and Edge are same sound “ha-shi” in Japanese.
Fence and Hmm are same sound “Hee” in Japanese.
A steam locomotive, your company, returning to office and writers are same sound “Kisha” in Japanese.

Wikipedia English version does not returns synonyms branch page if the search keyword has synonyms. It returns a default matched word.

Now, Japanese version script can consider synonyms and get tags with synonyms.

Title:Search a keyword from Wikipedia and get category tags v1e
– Created 2017-05-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4669

set aRes1 to getCategoryFromKeyword(“AppleScript”) of me
–>  {”Mac OS development”, “MacOS development”, “Proprietary software”, “Scripting languages”}

on getCategoryFromKeyword(aText)
  set wikiStr to getBodyFromWikipedia(aText) of me
  
set aList to categoryTagListFromWikipedia(wikiStr, “[[Category:”) of me
  
set bList to cleanupArray(aList, “[[Category:”, “]]”) of me
  
return bList
end getCategoryFromKeyword

on categoryTagListFromWikipedia(sText, pickUpKey)
  set aString to current application’s NSString’s stringWithString:sText
  
set anArray to aString’s componentsSeparatedByString:(string id 10)
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“SELF BEGINSWITH %@”, pickUpKey)
  
return (anArray’s filteredArrayUsingPredicate:aPredicate) as list
end categoryTagListFromWikipedia

–Search keyword with Wikipedia. Without considering Synonyms
on getBodyFromWikipedia(aText)
  –set reqURLStr to “https://jp.wikipedia.org/w/api.php” –Japanese Version URL
  
set reqURLStr to “https://en.wikipedia.org/w/api.php” –English Version URL
  
  
set aRec to {action:“query”, titles:aText, |prop|:“revisions”, rvprop:“content”, |format|:“json”}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
if aRes = missing value then return missing value
  
  
set aRESTres1 to (json of aRes)
  
try
    set aRESTres to pages of query of aRESTres1
  on error
    return missing value
  end try
  
  
set allKeys to (aRESTres’s allKeys()) as list
  
repeat with i in allKeys
    set j to contents of i
    
set aKeyPath to j & “.” & “revisions.*”
    
set aContent to (aRESTres’s valueForKeyPath:aKeyPath)
    
return aContent as string
  end repeat
end getBodyFromWikipedia

on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

on cleanupArray(aList, fromStr, toStr)
  set newList to {}
  
  
repeat with i in aList
    set b1 to repChar(i, fromStr, “”) of me
    
set b2 to repChar(b1, toStr, “”) of me
    
    
set b3 to offset of “|” in b2
    
if b3 is not equal to 0 then
      set b2 to text 1 thru (b3 - 1) of b2
    end if
    
set the end of newList to b2
  end repeat
  
  
return newList
end cleanupArray

–Pure AppleScript Version
on repChar(origText, targStr, repStr)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

★Click Here to Open This Script 

2017/05/27 Wikipediaで検索を行ってキーワードのカテゴリを取得する v1

与えられたキーワードをWikipediaで検索して、キーワードのカテゴリ名を取得するAppleScriptです。

何かのキーワードのカテゴリを取得するのに、いろいろプログラムやシソーラス辞書を検索することも試してみましたが、もっと乱暴かつ粗暴で即物的で低レベルな解決方法を見つけたので試してみました。

wiki0_resized.png

WikipediaをREST API経由で検索して、本文を取得し、文末にあるカテゴリ情報を取得すればいい感じのカテゴリ情報になるんでは? と思いつき、試してみたところ、、、うまく行ってしまいました(汗)

wiki1_resized.png

まだ、キーワードのフォワーディング(Mac OS X→macOS)、同音異義語や類義語への分岐(カール→複数の同音異義語への分岐)には対応していませんが、対応するのも(パターンがわかれば)それほど難しくなさそうです。

→ 実際にやってみたら割と簡単に処理できてしまいました(汗) 

AppleScript名:Wikipediaで検索を行ってキーワードのカテゴリを取得する v1
– Created 2017-05-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–https://www.mediawiki.org/wiki/API:Main_page/ja
–http://piyocast.com/as/archives/4666

set aRes1 to getCategoryFromKeyword(“カール (スナック菓子)”) of me
–>  {”スナック菓子の商品名”, “明治 (企業)”, “登録商標”}

on getCategoryFromKeyword(aText)
  set wikiStr to getBodyFromWikipedia(aText) of me
  
set aList to categoryTagListFromWikipedia(wikiStr, “[[Category:”) of me
  
set bList to cleanupArray(aList, “[[Category:”, “]]”) of me
  
return bList
end getCategoryFromKeyword

on categoryTagListFromWikipedia(sText, pickUpKey)
  set aString to current application’s NSString’s stringWithString:sText
  
set anArray to aString’s componentsSeparatedByString:(string id 10)
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“SELF BEGINSWITH %@”, pickUpKey)
  
return (anArray’s filteredArrayUsingPredicate:aPredicate) as list
end categoryTagListFromWikipedia

–Wikipediaで指定キーワードの検索を行う(同義語、類義語などへの分岐に未対応)
on getBodyFromWikipedia(aText)
  set reqURLStr to “https://jp.wikipedia.org/w/api.php” –Japanese Version
  
  
set aRec to {action:“query”, titles:aText, |prop|:“revisions”, rvprop:“content”, |format|:“json”}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
set aRESTres to (pages of query of json of aRes)
  
  
set allKeys to (aRESTres’s allKeys()) as list
  
repeat with i in allKeys
    set j to contents of i
    
set aKeyPath to j & “.” & “revisions.*”
    
set aContent to (aRESTres’s valueForKeyPath:aKeyPath)
    
return aContent as string
  end repeat
end getBodyFromWikipedia

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

on cleanupArray(aList, fromStr, toStr)
  set newList to {}
  
  
repeat with i in aList
    set b1 to repChar(i, fromStr, “”) of me
    
set b2 to repChar(b1, toStr, “”) of me
    
    
set b3 to offset of “|” in b2
    
if b3 is not equal to 0 then
      set b2 to text 1 thru (b3 - 1) of b2
    end if
    
set the end of newList to b2
  end repeat
  
  
return newList
end cleanupArray

–Pure AppleScript Version
on repChar(origText, targStr, repStr)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

★Click Here to Open This Script 

2017/05/27 書式つきテキストを組み立てて、クリップボードに設定(影をつける)

書式つきテキスト(NSAttributedString)を組み立てて、クリップボードに設定するAppleScriptの改良版です。影(NSShadow)の追加指定を行ってみました。

stylet2.png

AppleScript名:書式つきテキストを組み立てて、クリップボードに設定(影をつける)
– Created 2017-05-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
–http://piyocast.com/as/archives/4664

set anAttrStr to makeRTFfromParameters("ぴよまるソフトウェア", 2.0, 36) of me

set attrList to {anAttrStr}
restoreClipboard(attrList) of me –set the clipboard to rtf_data

–書式つきテキストを組み立てる
on makeRTFfromParameters(aStr as string, outlineNum as real, aFontSize as real)
  –フォント
  
set aVal1 to current application’s NSFont’s fontWithName:"HiraKakuStdN-W8" |size|:aFontSize
  
set aKey1 to (current application’s NSFontAttributeName)
  
  
–色
  
set aVal2 to current application’s NSColor’s blackColor()
  
set aKey2 to (current application’s NSForegroundColorAttributeName)
  
  
–カーニング
  
set aVal3 to -2.0
  
set akey3 to (current application’s NSKernAttributeName)
  
  
–アンダーライン
  
set aVal4 to 0
  
set akey4 to (current application’s NSUnderlineStyleAttributeName)
  
  
–リガチャ
  
set aVal5 to 2 –全てのリガチャを有効にする
  
set akey5 to (current application’s NSLigatureAttributeName)
  
  
–枠線(アウトライン)
  
set aVal6 to outlineNum
  
set akey6 to (current application’s NSStrokeWidthAttributeName)
  
  
–Shadow
  
set aVal7 to current application’s NSShadow’s alloc()’s init()
  
aVal7’s setShadowOffset:(current application’s CGSizeMake(1.0, 1.0)) –影のサイズ
  
aVal7’s setShadowColor:(current application’s NSColor’s blackColor()) –影の色
  
aVal7’s setShadowBlurRadius:2.0 –ぼかしの半径
  
set akey7 to (current application’s NSShadowAttributeName)
  
  
set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey6, akey7}
  
set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aVal6, aVal7}
  
set attrsDictionary to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
  
  
set attrStr to current application’s NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
  
return attrStr
  
end makeRTFfromParameters

–From Shane Stanley’s Book "Everyday AppleScriptObjC"
on restoreClipboard(theArray as list)
  set thePasteboard to current application’s NSPasteboard’s generalPasteboard()
  
thePasteboard’s clearContents()
  
thePasteboard’s writeObjects:theArray
end restoreClipboard

★Click Here to Open This Script 

2017/05/26 書式つきテキストを組み立てて、クリップボードに設定

書式つきテキスト(NSAttributedString)を組み立てて、クリップボードに設定するAppleScriptです。

作成した書式つきテキストは、そのままKeynoteなどのアプリケーションにペーストして利用することが可能です。グラフィックではないため、アプリケーション上での再編集や影の指定、色の変更などが可能です。

styles.png

Keynoteでアウトライン文字を使いたいことはままあるので、このように手軽にクリップボードに転送して利用できると実用性が高いことでしょう。

後日談:という紹介をしたら、KeynoteのヘビーユーザーからKeynoteのGUI上からアウトライン文字の書式を指定できることを教えてもらいました。

AppleScript名:書式つきテキストを組み立てて、クリップボードに設定
– Created 2017-05-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4662

set anAssrStr to makeRTFfromParameters(“ぴよまるソフトウェア”, 3.0, 72) of me

set attrList to {anAssrStr}
restoreClipboard(attrList) of me –set the clipboard to rtf_data

–書式つきテキストを組み立てる
on makeRTFfromParameters(aStr as string, outlineNum as real, aFontSize as real)
  –フォント
  
set aVal1 to current application’s NSFont’s fontWithName:“HiraKakuStdN-W8″ |size|:aFontSize
  
set aKey1 to (current application’s NSFontAttributeName)
  
  
–色
  
set aVal2 to current application’s NSColor’s blueColor()
  
set aKey2 to (current application’s NSForegroundColorAttributeName)
  
  
–カーニング
  
set aVal3 to -2.0
  
set akey3 to (current application’s NSKernAttributeName)
  
  
–アンダーライン
  
set aVal4 to 0
  
set akey4 to (current application’s NSUnderlineStyleAttributeName)
  
  
–リガチャ
  
set aVal5 to 2 –全てのリガチャを有効にする
  
set akey5 to (current application’s NSLigatureAttributeName)
  
  
–枠線(アウトライン)
  
set aVal6 to outlineNum
  
set akey6 to (current application’s NSStrokeWidthAttributeName)
  
  
set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey6}
  
set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aVal6}
  
set attrsDictionary to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
  
  
set attrStr to current application’s NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
  
return attrStr
  
end makeRTFfromParameters

–From Shane Stanley’s Book “Everyday AppleScriptObjC”
on restoreClipboard(theArray as list)
  set thePasteboard to current application’s NSPasteboard’s generalPasteboard()
  
thePasteboard’s clearContents()
  
thePasteboard’s writeObjects:theArray
end restoreClipboard

★Click Here to Open This Script 

2017/05/24 restcountriesですべての国の情報を取得する

REST API経由で世界の国情報を配信しているrestcountries.euのサービスを呼び出して、すべての国情報を取得するAppleScriptです。

restcountriesのREST APIサービスについては、ひととおりすべての機能をAppleScriptから呼び出せることを確認しています。

国情報についてはそれほど変更があるわけでもないので(人口などの統計は年単位でしかアップデートされないでしょうし、首都などもコロコロ変わるわけではなく)、いっそOS側にビルトインで最初から用意されていてもいい気がします。

アプリケーションの各国語ローカライズを行う場合などに国情報は参考にするので、意外と国情報にREST API経由でアクセスできるのは助かります。

→ 調査してみたところ、オープンソースの「countries」というプロジェクトでデータが公開されているのを見つけました。JSON形式のデータも入っているので、このあたりをローカルで小突き回して加工するのがよさそうです。

AppleScript名:ALL(すべての国の情報を取得する)
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4660

set reqURLStr to “https://restcountries.eu/rest/v2/all”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESCode to (responseCode of aRes) as integer
if aRESCode is not equal to 200 then return false

set aRESTres to (json of aRes)

return aRESTres
–> {{alpha2Code:”AF”, alpha3Code:”AFG”, nativeName:”افغانستان“, region:”Asia”, borders:{”IRN”, “PAK”, “TKM”, “UZB”, “TJK”, “CHN”}, numericCode:”004″, currencies:{{name:”Afghan afghani”, symbol:”؋“, code:”AFN”}}, translations:{fr:”Afghanistan”, pt:”Afeganistão”, de:”Afghanistan”, it:”Afghanistan”, es:”Afganistán”, br:”Afeganistão”, ja:”アフガニスタン”}, flag:”https://restcountries.eu/data/afg.svg”, name:”Afghanistan”, callingCodes:{”93″}, timezones:{”UTC+04:30″}, subregion:”Southern Asia”, altSpellings:{”AF”, “Afġānistān”}, topLevelDomain:{”.af”}, population:27657145, area:652230.0, regionalBlocs:{{otherNames:{}, otherAcronyms:{}, acronym:”SAARC”, name:”South Asian Association for Regional Cooperation”}}, latlng:{33.0, 65.0}, capital:”Kabul”, gini:27.8, demonym:”Afghan”, languages:{{nativeName:”پښتو“, iso639_2:”pus”, name:”Pashto”, iso639_1:”ps”}…

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

2017/05/20 最小公倍数を求める

2つの数の最小公倍数を求めるAppleScriptです。

最大公約数や素数チェックや素因数分解を作ったので、ついでにこの手の計算を行ってみました。

AppleScript名:最小公倍数を求める
– Created 2017-05-20 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4654

set a1Num to 75
set b1Num to 315
set cRes to getLCMbetweenTwoNums(a1Num, b1Num) of me
–> 1575

–2つの数の最小公倍数を求める
on getLCMbetweenTwoNums(a1Num, b1Num)
  set aGCD to getGCDbetweenTwoNums(a1Num, b1Num) of me
  
set a2Num to a1Num div aGCD
  
set b2Num to b1Num div aGCD
  
return (aGCD * a2Num * b2Num)
end getLCMbetweenTwoNums

–2つの数の最大公約数を求める
on getGCDbetweenTwoNums(aNum, bNum)
  set aRes to getPrimeFactor(aNum) of me
  
set bRes to getPrimeFactor(bNum) of me
  
  
–2つのリストを集合として扱い、積集合を求める
  
set aSet to current application’s NSMutableSet’s setWithArray:aRes
  
set bSet to current application’s NSMutableSet’s setWithArray:bRes
  
aSet’s intersectSet:bSet
  
set cRes to aSet’s allObjects() as list
  
  
set dRes to multipleListElements(cRes) of me
  
return dRes
end getGCDbetweenTwoNums

–与えられた数を素因数分解する
on getPrimeFactor(aTargNum)
  copy aTargNum to a
  
set pFactList to {}
  
  
repeat
    set pRes to checkPrimeNumber(a) of me
    
if pRes = true then
      set the end of pFactList to a
      
return pFactList
    end if
    
    
set aFactor to checkFactor(a) of me
    
set the end of pFactList to aFactor
    
set a to a div aFactor
  end repeat
end getPrimeFactor

–素数チェック
on checkPrimeNumber(aNum)
  repeat with i from 2 to aNum - 1
    if aNum mod i is equal to 0 then
      return false
    end if
  end repeat
  
return true
end checkPrimeNumber

–素因数チェック
on checkFactor(aNum)
  repeat with i from 2 to aNum - 1
    if aNum mod i is equal to 0 then
      return i
    end if
  end repeat
end checkFactor

–リスト内の要素をすべて乗算する
on multipleListElements(aList)
  set aNum to 1
  
set aRes to 1
  
repeat with i in aList
    set aRes to aRes * (aNum * i)
  end repeat
  
return aRes
end multipleListElements

★Click Here to Open This Script 

2017/05/20 最大公約数を求めるv2

2つの数の最大公約数を求めるAppleScriptです。

素数チェックや素因数分解を作ったので、この手の計算を行ってみました。

AppleScript名:最大公約数を求めるv2
– Created 2017-05-20 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4653

set aRes to getGCDbetweenTwoNums(12, 18) of me
–> 6

–2つの数の最大公約数を求める
on getGCDbetweenTwoNums(aNum, bNum)
  set aRes to getPrimeFactor(aNum) of me
  
set bRes to getPrimeFactor(bNum) of me
  
  
–2つのリストを集合として扱い、積集合を求める
  
set aSet to current application’s NSMutableSet’s setWithArray:aRes
  
set bSet to current application’s NSMutableSet’s setWithArray:bRes
  
aSet’s intersectSet:bSet
  
set cRes to aSet’s allObjects() as list
  
  
set dRes to multipleListElements(cRes) of me
  
return dRes
end getGCDbetweenTwoNums

–与えられた数を素因数分解する
on getPrimeFactor(aTargNum)
  copy aTargNum to a
  
set pFactList to {}
  
  
repeat
    set pRes to checkPrimeNumber(a) of me
    
if pRes = true then
      set the end of pFactList to a
      
return pFactList
    end if
    
    
set aFactor to checkFactor(a) of me
    
set the end of pFactList to aFactor
    
set a to a div aFactor
  end repeat
end getPrimeFactor

–素数チェック
on checkPrimeNumber(aNum)
  repeat with i from 2 to aNum - 1
    if aNum mod i is equal to 0 then
      return false
    end if
  end repeat
  
return true
end checkPrimeNumber

–素因数チェック
on checkFactor(aNum)
  repeat with i from 2 to aNum - 1
    if aNum mod i is equal to 0 then
      return i
    end if
  end repeat
end checkFactor

–リスト内の要素をすべて乗算する
on multipleListElements(aList)
  set aNum to 1
  
set aRes to 1
  
repeat with i in aList
    set aRes to aRes * (aNum * i)
  end repeat
  
return aRes
end multipleListElements

★Click Here to Open This Script 

2017/05/20 1箇所から別の箇所の方位を求める

1箇所から別の箇所の方位を求めるAppleScriptです。

主な三角関数を呼び出すためにBridgePlus AppleScript Librariesを、atan2関数を呼ぶためにSatimage OSAXを利用しています。

まだ正確かどうか検証しながら使っている状態なので、正しく方位が計算できているかわかりません。間違っている箇所がある可能性も否定できません。

テスト用の緯度経度情報は、住所情報からYaooの住所ジオコーダーAPIを使って求めました(日本測地系で)。

atan2はこういう用途で必要なので(こういう用途でもないと使わなさそうですが)、BridgePlusの機能に組み込んでもらえるようにShane Stanleyにお願いしているところです。

いっそ、Numbersで組み込み関数を一気に増やしたCephes Mathematical Libraryでも呼び出せるようにするといいのに、とは思っていますが・・・Cの関数ライブラリなのでObjective-Cでいったんラッピングする必要があるので大変そうです。

geo1_resized.png

geo2_resized.png

geo3_resized.png

geo4_resized.png

geo5_resized.png

AppleScript名:1箇所から別の箇所の方位を求める
– Created 2017-04-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use bPlus : script “BridgePlus”
–http://zurb.com/forrst/posts/Direction_between_2_CLLocations-uAo
–http://piyocast.com/as/archives/4647

set coord1 to {latitude:35.73677496, longitude:139.63754457} –中村橋駅

set coord2 to {latitude:35.78839012, longitude:139.61241447} –和光市駅
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  -25.925429877542

set coord2 to {latitude:35.7227821, longitude:139.63860897} –鷺宮駅
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  175.649833487804

set coord2 to {latitude:35.73590542, longitude:139.62986745} –富士見台駅
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  -96.385293928667

set coord2 to {latitude:35.73785024, longitude:139.65339321} –練馬駅
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  85.959474671834

set coord2 to {latitude:35.71026838, longitude:139.81215754} –東京スカイツリー
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  96.826542737106

–位置情報1から位置情報2の方角を計算。北が0度
on calcDirectionBetweenTwoPlaces(coord1, coord2)
  load framework
  
set deltaLong to (longitude of coord2) - (longitude of coord1)
  
set yComponent to bPlus’s sinValueOf:deltaLong
  
set xComponent to (bPlus’s cosValueOf:(latitude of coord1)) * (bPlus’s sinValueOf:(latitude of coord2)) - (bPlus’s sinValueOf:(latitude of coord1)) * (bPlus’s cosValueOf:(latitude of coord2)) * (bPlus’s cosValueOf:deltaLong)
  
  
set vList to {yComponent, xComponent}
  
set radians to atan2 vList —Requires SatImage OSAX
  
set degreeRes to (radToDeg(radians) of me)
  
  
return degreeRes
end calcDirectionBetweenTwoPlaces

on radToDeg(aRadian)
  return aRadian * (180 / pi)
end radToDeg

★Click Here to Open This Script 

2017/05/16 MatRによる行列データの取り扱い

MatR(By David Cox)をFramework化した「DCMatrix.framework」を呼び出して行列データのハンドリングを行うAppleScriptです。

行列データの作成、入力、演算、出力などをひととおり試してみたものです(野球でいえば、素振りのレベル。でも、これをやっておかないと高度なScriptが組めない、、、)。

OS X 10.10以降用にビルドしたフレームワークのバイナリを用意しておきました。興味のある方は自己責任で~/Library/Frameworksフォルダに入れてお試しください。

–> Download Framework Binary

AppleScriptで行列を扱うのに、仏Satimage Softwareが提供しているOSAX「Numerics OSAX」を用いるのが一般的です。

ただ、数値変数の表現レンジ(指数表示を使わずに表現できる幅)が1E10ぐらいのAppleScriptの数値演算で、行列が扱えても実用上どうなんだろうと疑問に思っておりました。Numerics OSAX内で高精度の数値型のデータが使えるようではあるものの、外部とやりとりする場合には当然のごとく精度が落ちてしまいます。

# Satimage SoftwareのNumerics OSAXは、高速フーリエ変換とか画像処理とか、3次元配列の計算など、私には使い切れないほど高度な演算を提供してくれるOSAXなので、ぜひ知っておいていただきたいものです

ときに、そんなAppleScriptの数値ハンドリング機能でも、機械学習などの分野においては数値の表現レンジが大きくなくてよいので、割とよさそうです。

そうした利用分野では、CSVやタブ区切りテキストファイルから行列データを取得するケースが多いので、ファイルや配列から行列データを作成する手段を探していました。機械学習、とまで気合を入れなくても線形回帰分析(Linear Regression)を行うための準備として、このMatRを試してみたものです。

線形回帰分析の原理は、KeynoteやNumbersのグラフ「分散図」で実感することができます。「月ごとの平均気温とビール消費量の関係」「広告宣伝費と来店者数」など、グラフ化してみるととてもよく相関が見てとれます。

sample_graph.png

AppleScript名:MatRのじっけん v1
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “DCMatrix”
–https://github.com/davidcox95/MatR
–http://piyocast.com/as/archives/4640

set aMatrix to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:5) andRows:3 byColumns:4
set aList to (aMatrix’s |print|())
(*
5 5 5 5
5 5 5 5
5 5 5 5
*)

set bMatrix to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:3) andRows:4 byColumns:3
set bScalar to current application’s NSNumber’s numberWithDouble:2
set bRes to bMatrix’s addScalar:bScalar
bRes’s |print|()
(*
5 5 5
5 5 5
5 5 5
5 5 5
*)

set cScalar to current application’s NSNumber’s numberWithDouble:2
set cRes to bMatrix’s subtractScalar:cScalar
cRes’s |print|()
(*
1 1 1
1 1 1
1 1 1
1 1 1
*)

set dScalar to current application’s NSNumber’s numberWithDouble:2
set eRes to bMatrix’s multiplyScalar:dScalar
eRes’s |print|()
(*
6 6 6
6 6 6
6 6 6
6 6 6
*)

set fScalar to current application’s NSNumber’s numberWithDouble:2
set fRes to bMatrix’s divideScalar:fScalar
fRes’s |print|()
(*
1.5 1.5 1.5
1.5 1.5 1.5
1.5 1.5 1.5
1.5 1.5 1.5
*)

set m4 to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:4) andRows:3 byColumns:3
set m2 to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:2) andRows:3 byColumns:3
set gRes to m4’s addMatrix:m2
gRes’s |print|()
(*
6 6 6
6 6 6
6 6 6
*)

set hRes to m4’s subtractMatrix:m2
hRes’s |print|()
(*
2 2 2
2 2 2
2 2 2
*)

set iRes to m4’s multiplyMatrix:m2
iRes’s |print|()
(*
24 24 24
24 24 24
24 24 24
*)

★Click Here to Open This Script 

AppleScript名:MatRのじっけん v2
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “DCMatrix”
–https://github.com/davidcox95/MatR
–http://piyocast.com/as/archives/4640

set aMatrix to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:5) andRows:3 byColumns:4
set aList to doubleListFromMatrix(aMatrix) of me
–>  {{5.0, 5.0, 5.0, 5.0}, {5.0, 5.0, 5.0, 5.0}, {5.0, 5.0, 5.0, 5.0}}

set bList to intListFromMatrix(aMatrix) of me
–>  {{5, 5, 5, 5}, {5, 5, 5, 5}, {5, 5, 5, 5}}

set cList to strListFromMatrix(aMatrix) of me
–>  {{”5″, “5″, “5″, “5″}, {”5″, “5″, “5″, “5″}, {”5″, “5″, “5″, “5″}}

set bMatrix to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:3) andRows:4 byColumns:3
set bScalar to current application’s NSNumber’s numberWithDouble:2
set bRes to bMatrix’s addScalar:bScalar
set bbRes to intListFromMatrix(bRes) of me
–>  {{5, 5, 5}, {5, 5, 5}, {5, 5, 5}, {5, 5, 5}}

set cScalar to current application’s NSNumber’s numberWithDouble:2
set cRes to bMatrix’s subtractScalar:cScalar
set ccRes to intListFromMatrix(cRes) of me
–>  {{1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}}

set dScalar to current application’s NSNumber’s numberWithDouble:2
set eRes to bMatrix’s multiplyScalar:dScalar
set eeRes to intListFromMatrix(eRes) of me
–>  {{6, 6, 6}, {6, 6, 6}, {6, 6, 6}, {6, 6, 6}}

set fScalar to current application’s NSNumber’s numberWithDouble:2
set fRes to bMatrix’s divideScalar:fScalar
set ffRes to doubleListFromMatrix(fRes) of me
–>  {{1.5, 1.5, 1.5}, {1.5, 1.5, 1.5}, {1.5, 1.5, 1.5}, {1.5, 1.5, 1.5}}

set m4 to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:4) andRows:3 byColumns:3
set m2 to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:2) andRows:3 byColumns:3
set gRes to m4’s addMatrix:m2
set ggRes to intListFromMatrix(gRes) of me
–>  {{6, 6, 6}, {6, 6, 6}, {6, 6, 6}}

set hRes to m4’s subtractMatrix:m2
set hhRes to intListFromMatrix(hRes) of me
–>  {{2, 2, 2}, {2, 2, 2}, {2, 2, 2}}

set iRes to m4’s multiplyMatrix:m2
set iiRes to intListFromMatrix(iRes) of me
–>  {{24, 24, 24}, {24, 24, 24}, {24, 24, 24

on doubleListFromMatrix(aMatrix)
  set aList to {}
  
set aRows to aMatrix’s rows()
  
set aColumns to aMatrix’s columns()
  
  
repeat with rowC from 0 to (aRows - 1)
    set colList to {}
    
repeat with colC from 0 to (aColumns - 1)
      set the end of colList to (aMatrix’s getElementAtRow:rowC andColumn:colC) as real
    end repeat
    
set the end of aList to colList
  end repeat
  
return aList
end doubleListFromMatrix

on intListFromMatrix(aMatrix)
  set aList to {}
  
set aRows to aMatrix’s rows()
  
set aColumns to aMatrix’s columns()
  
  
repeat with rowC from 0 to (aRows - 1)
    set colList to {}
    
repeat with colC from 0 to (aColumns - 1)
      set the end of colList to (aMatrix’s getElementAtRow:rowC andColumn:colC) as integer
    end repeat
    
set the end of aList to colList
  end repeat
  
return aList
end intListFromMatrix

on strListFromMatrix(aMatrix)
  set aList to {}
  
set aRows to aMatrix’s rows()
  
set aColumns to aMatrix’s columns()
  
  
repeat with rowC from 0 to (aRows - 1)
    set colList to {}
    
repeat with colC from 0 to (aColumns - 1)
      set the end of colList to (aMatrix’s getElementAtRow:rowC andColumn:colC) as string
    end repeat
    
set the end of aList to colList
  end repeat
  
return aList
end strListFromMatrix

★Click Here to Open This Script 

AppleScript名:MatRのじっけん v3
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “DCMatrix”
–https://github.com/davidcox95/MatR
–http://piyocast.com/as/archives/4640

–listからMatrixを作る
set mList to {{1, 2, 3, 4}, {11, 12, 13, 14}, {21, 22, 23, 24}}
set aMatrix to current application’s |Matrix|’s alloc()’s initWithArray:mList andRows:3 byColumns:4
aMatrix’s |print|()
(*
1 2 3 4
11 12 13 14
21 22 23 24
*)

set a1Res to aMatrix’s |transpose|() –左回転
(
a1Res’s |print|())
(*
1 11 21
2 12 22
3 13 23
4 14 24
*)

★Click Here to Open This Script 

tab_sep_text.png

AppleScript名:MatRのじっけん v4
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “DCMatrix”
–https://github.com/davidcox95/MatR
–http://piyocast.com/as/archives/4640

–タブ区切りテキストファイルからMatrixを作る
set aPosixFile to POSIX path of (choose file)
set aMatrix to current application’s |Matrix|’s alloc()’s initWithContentsFromFile:aPosixFile
aMatrix’s |print|()
(*
5 6 7 8
3 4 5 6
1 2 3 4
*)

★Click Here to Open This Script 

2017/05/14 Twitter ScripterでAppleScriptからTwitter投稿、検索

AppleScriptから利用する専用のGUIなしTwitterクライアント「Twitter Scripter」(By Mousedown)を呼び出してTwitter投稿や検索を行うAppleScriptです。

Twitter Scripterはフリーでダウンロードして利用できます。Twitter Scripterは本当にAppleScriptから呼び出すためだけに作られているので、ユーザーインタフェースは一切ありません。

tw1.png

システム環境設定から確認できる(macOS 10.12, Sierra)、Twitterアカウントの一覧を取得します。Twitterアカウントは複数登録されている可能性があるため、リスト(配列)で返ってきます。

AppleScript名:Twitterアカウントを取得
–http://piyocast.com/as/archives/4636
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
–> {”piyomaru”}
end tell

★Click Here to Open This Script 

さらに、得られたアカウント一覧から適当なものを選んでTwitter投稿してみましょう。

AppleScript名:Twitterにアカウントを指定して投稿(テキストのみ)
–http://piyocast.com/as/archives/4636
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
–> {”piyomaru”}
  
  
if length of availableAccounts = 0 then return
  
  
set twitterAccount to first item of availableAccounts
  
–> “piyomaru”
  
  
tweet “てすと” using account twitterAccount
end tell

★Click Here to Open This Script 

twitter2.png

システムからTwitterアカウントを取得しなくても、直接文字列で指定することも(当然)できます。ただし、自分で所有してOSに登録していないTwitterアカウントを指定するとエラー応答が返ってきます。

–> {response:{{}}, action:false}

AppleScript名:Twitterにアカウントを指定して投稿(テキストのみ2)
–http://piyocast.com/as/archives/4636
tell application “Twitter Scripter”
  tweet “てすと” using account “piyomaru”
end tell

★Click Here to Open This Script 

Twitter Scripterからはコマンド実行時に大量のデータが返ってくるため、Cocoaの機能を利用してデータの絞り込みを行うのにぴったりです。むしろ、Cocoaの機能を使わないと大量のデータからのしぼりこみを行うのは大変です。

画像を指定してテキストと一緒に画像を投稿することもできるのですが、画像添付についてはうまく動くときと画像が投稿されないときがあるようです(なんで? どうして???)。

検索も可能で、出力対象にrecent(最近の投稿から)、popular(話題の投稿から)、mixed(デフォルト)の3タイプから選択可能です。出力件数には数値で自由に指定できるようになっていますが、実際にためしてみたところ最大件数は100件のようです。

AppleScript名:Twitterを検索する
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
if length of availableAccounts = 0 then return
  
  
set anAccount to first item of availableAccounts
  
search for “戦場の絆” using account anAccount returning entries 10 result type “mixed”
end tell

★Click Here to Open This Script 

AppleScript名:Twitterを検索する(最近の投稿から)
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
if length of availableAccounts = 0 then return
  
  
set anAccount to first item of availableAccounts
  
search for “戦場の絆” using account anAccount returning entries 10 result type “recent” –最近の投稿
end tell

★Click Here to Open This Script 

AppleScript名:Twitterを検索する(話題の投稿から)
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
if length of availableAccounts = 0 then return
  
  
set anAccount to first item of availableAccounts
  
search for “戦場の絆” using account anAccount returning entries 10 result type “popular” –話題の投稿
end tell

★Click Here to Open This Script 

その他のサンプルScriptもMousedownのWebサイトに掲載されています。

2017/04/28 えほんシリーズ、続刊作成中

AppleScriptえほんシリーズ(1)として作成した「iTunes Control」の続刊が「Keynote Control」(近日刊行予定)です。

ただし、分量が割と増えたために2分冊になります。難易度を上げずに丁寧に説明すると、分量が増えてしまいます(汗)。

keynote1.png
▲Keynote Control (1)

keynote1a.png
▲Keynote Control (1) もくじ ※作成中のため、変更になる可能性もあります

keynote2.png
▲Keynote Control (2) 表紙

Keynote Control 1では、Keynoteの基礎的なオブジェクトへのアクセスを体験。
続刊のKeynote Control 2では、より実践的なオブジェクトのアクセスを体験できるようになります。

そして、この2冊は同時に出る予定です(1冊はほぼ書きあがったので2冊目に着手)。本シリーズは、ある程度バリエーションが増えないと価値が出てこないので、アイテム数を増やす方向で作業をすすめています。

「その次」についての候補があればぜひご意見をお寄せください。ダミーで表紙を作ってみた感じではありますが、こんな感じ(↓)のものもありえそうで。

airpods_control.png

2017/04/26 指定画像の余白の自動トリミング

オープンソースのKGPixelBoundsClip(By David Keegan)をフレームワーク化した「KGPixelBoundsClipKit」を呼び出して指定画像を自動でトリミングするAppleScriptです。

OS X 10.10以降用にビルドしたフレームワークのバイナリを用意しておきました。興味のある方は自己責任で~/Library/Frameworksフォルダに入れてお試しください。

–> Download Framework Binary

指定画像(背景を透明に指定したPNGで実行)を自動トリミングして、デスクトップに結果を書き出します。

Photoshopにも同様の機能があったような記憶がありますが、フレームワーク経由で実行することで並列処理を行いやすい部品を作っておくことが目的です。

original_and_trimed.png

AppleScript名:指定画像の余白の自動トリミング
– Created 2017-04-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “KGPixelBoundsClipKit” –https://github.com/kgn/KGPixelBoundsClip
–http://piyocast.com/as/archives/4611

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

set bImage to anImage’s imageClippedToPixelBounds()

set aDesktopPath to (current application’s NSProcessInfo’s processInfo()’s environment()’s objectForKey:(“HOME”))’s stringByAppendingString:“/Desktop/”
set savePath to aDesktopPath’s stringByAppendingString:((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.png”)
set fRes to saveNSImageAtPathAsPNG(bImage, savePath) of me

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

★Click Here to Open This Script 

2017/04/21 Safariの最前面のウィンドウで表示している内容をMacJournalの選択中のjournalに新規エントリを作成して入れ

Safariの最前面のウィンドウで表示しているページのテキストとタイトルで、MacJournalの選択中のJournalに新規エントリを作成するAppleScriptです。

mjournal1.png

さまざまなデータ処理を行うさいのコーパス(例文)を集めるために作成したものです。本来は指定URLをリストで保持しておいて、そこから順次Webページを表示して複数のページを一気に処理するAppleScriptを作ったときに「ついで」に作成。

SafariからWebサイトのテキストを取得するのは、なかなか大変です。

何が大変かといえば、「取得そのもの」ではなく、「取得した内容」が本当に文字化けしていないかどうか、チェックする必要があるということです。とくに、古いWebサイトだと文字コードの指定が不十分で、自動判別できないケースもあります

とりあえず、その処理は省略して(すでに書いてあるので)、MacJournalの中にWebサイトから取得した本文を入れてみることにしました。

すべて自動で連続して作成したときには問題はなかったのですが、「現在表示中のWebサイトの内容を現在選択中のMacJournalのジャーナルに新規エントリに作成」とかいう、1回実行するだけのScriptを走らせてみたところ、予想外の挙動がありました。

選択中のジャーナル(フォルダみたいなもの)の中に新規エントリ(記事)を作成すると、そのエントリへのオブジェクト参照が返ってくることが期待できます。ところが、MacJournalで実行したところ、なぜか以前に作成したエントリに内容が上書きされてしまいました。

おそらく、内部処理のバグだと思うのですが、本来であればID(固有値)が返ってくるべきところでindex(順番で数えた数)が返ってきてしまっている様子。エントリの新規作成時の返り値をそのまま使うとだまされるわけです。

そこで、新規作成エントリが選択中される(selected entry)ことを利用し、その選択中のエントリに対してタイトルと本文を設定することで、新規作成エントリを指定しないで対処しました。

何も考えずに単にObjective-Cの解釈と書き換えを行うだけのAppleScriptObjCのプログラムよりも、出来の悪いGUIアプリケーションを相手に対策を考えることのほうがプログラミングっぽい感じがします。

AppleScript名:Safariの最前面のウィンドウで表示している内容をMacJournalの選択中のjournalに新規エントリを作成して入れる
– Created 2017-04-21 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4598

–現在選択中のJournalを取得
tell application "MacJournal"
  tell document 1
    set curJournal to selected journal
  end tell
end tell

set titleRes to getPageTitleOfFrontmostWindow() of me
if titleRes = false then return

set textRes to getPageTextOfFrontmostWindow() of me
if textRes = false then return

tell application "MacJournal"
  tell curJournal
    set jRes to make new journal entry –ここで新規作成したentryへの参照を取得するが、どうも固定のIDではなくindexが返ってくるもよう
  end tell
  
  
tell selected entry
    set name to titleRes
    
set plain text content to textRes
  end tell
end tell

on getPageTextOfFrontmostWindow()
  tell application "Safari"
    if (count every window) = 0 then return false
    
tell window 1
      set aProp to properties
    end tell
    
set aDoc to document of aProp
    
set aText to text of aDoc
  end tell
  
return aText
end getPageTextOfFrontmostWindow

on getPageTitleOfFrontmostWindow()
  tell application "Safari"
    if (count every window) = 0 then return false
    
tell window 1
      set aProp to properties
    end tell
    
set aDoc to document of aProp
    
set aText to name of aDoc
  end tell
  
return aText
end getPageTitleOfFrontmostWindow

★Click Here to Open This Script 

2017/04/20 日本語音声認識テスト

日本語音声認識コマンドを待機、認識するAppleScriptです。

とくに日本語であることは指定していませんが、実行ユーザー環境が日本語環境で、Macに日本語音声認識エンジンがインストールされていて、サウンド入力デバイスが存在している場合には音声認識を行います(対象言語を指定して音声認識エンジンを起動できるといいのに。あるいは、与えるコマンド文字列から対応するエンジンが起動されるのか、、、、)。

OS側で用意している日本語音声認識によるAppleScript呼び出し機能よりも、素朴な機能ではありますが、音声認識文字列をリストで動的にAppleScript側から指定できる点や、他の音声認識機能の抑止指定が便利なこともあることでしょう。

recog1.png

普通に認識してくれます。認識フローティングウィンドウの「表示」をクリックすると、

recog2.png

指定したコマンド文字列一覧を表示します。

問題は音声認識の停止です。stopListening()を実行するとコマンド認識は停止するようですが、この認識フローティングウィンドウが消えないので、ちょっと困ります。

AppleScript名:日本語音声認識テスト
– Created 2017-04-20 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4595

set aCmdList to {“こんにちは”, “こんばんは”, “おはよう”}
–set aCmdList to {”Hello”, “goodnight”, “goodevening”}

set aRecognizer to current application’s NSSpeechRecognizer’s alloc()’s init()
aRecognizer’s setCommands:aCmdList
aRecognizer’s setDelegate:me
aRecognizer’s setListensInForegroundOnly:true
aRecognizer’s startListening()
aRecognizer’s setBlocksOtherRecognizers:true

on speechRecognizer:aSender didRecognizeCommand:aCmd
  set recogCmd to aCmd as string
  
say recogCmd using “Otoya” –Japanese Male Voice
  
–aSender’s stopListening()
end speechRecognizer:didRecognizeCommand:

★Click Here to Open This Script 

2017/04/19 NSColorからCIColorを作成

NSColorからCIColorを作成するAppleScriptです。

CoreImageの各種フィルタを利用して画像加工するさいに、色オブジェクトを指定するフィルタがいくつかあります。その際に指定するのがCIColor。そのため、NSColorからCIColorを求めるサンプルを書いてみました。

ただ、パラメータを指定してCIColorを作ってみても、たいして難しくもなんともないんですね(汗)

AppleScript名:NSColorからCIColorを作成
– Created 2017-04-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “QuartzCore”
use framework “AppKit”
–http://piyocast.com/as/archives/4591

set redValue to 0
set greenValue to 0
set blueValue to 1
set alphaVlaue to 1.0

set aNSColor to current application’s NSColor’s colorWithCalibratedRed:redValue green:greenValue blue:blueValue alpha:alphaVlaue
set aCIColor to current application’s CIColor’s alloc()’s initWithColor:aNSColor
–>  (CIColor) (0 0 1 1)

★Click Here to Open This Script 

AppleScript名:値を指定してCIColorを作成
– Created 2017-04-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “QuartzCore”
–http://piyocast.com/as/archives/4591

set redValue to 0
set greenValue to 0
set blueValue to 1
set alphaVlaue to 1.0

set aCIColor to current application’s CIColor’s alloc()’s initWithRed:redValue green:greenValue blue:blueValue alpha:alphaVlaue
–>  (CIColor) (0 0 1 1)

★Click Here to Open This Script 

2017/04/18 HelpBookの項目表示、キーワード検索

macOS上でヘルプを表示するHelpViewerはAppleScriptに対応しており、任意の項目の表示、キーワード検索、指定URLのオープンなどを行えるようになっています。

ただし、HelpViewerをAppleScriptから普通に操作したときの動作が怪しいので、Cocoa経由でも操作する方法を調べておきました。

HelpViewerで指定HelpBook中の指定のアンカーを表示させるだけであれば、

AppleScript名:help_sample
tell application “HelpViewer”
  activate
  
try
    lookup anchor “scpedt1126″ in book “com.apple.ScriptEditor.help”
  end try
end tell

★Click Here to Open This Script 

このぐらいの簡素な記述でOKです。これをCocoa経由で操作すると、

AppleScript名:指定アプリケーションのヘルプブック内の指定アンカーを表示する
– Created 2017-04-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4590

set anAppName to “Script Editor”
set aTargAnchor to “scpedt1126″ —-https://help.apple.com/scripteditor/mac/10.12/index.html?localePath=ja.lproj#/scpedt1126
set hRes to openHelpBook(anAppName, aTargAnchor) of me

–指定アプリケーションのヘルプブックで、指定アンカーを表示する
on openHelpBook(anAppName, aTargAnchor)
  set locBookName to getHelpBook(anAppName) of me
  
if locBookName = false then return false
  
current application’s NSHelpManager’s sharedHelpManager()’s openHelpAnchor:aTargAnchor inBook:locBookName
end openHelpBook

–指定アプリケーションのヘルプブック名称を取得する
on getHelpBook(anAppName)
  set aWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set appPath to aWorkspace’s fullPathForApplication:anAppName
  
if appPath is equal to missing value then return false
  
  
set locBookName to (current application’s NSBundle’s bundleWithPath:appPath)’s objectForInfoDictionaryKey:“CFBundleHelpBookName”
  
if locBookName is equal to missing value then return false
  
–> “com.apple.ScriptEditor.help”
  
return locBookName
end getHelpBook

★Click Here to Open This Script 

このぐらいになります。ただし、前者の記述ではHelpBook名称があらかじめわかっていることが前提、後者ではアプリケーション名からHelpBook名を取得して項目表示するという違いがあります。

問題はキーワード検索で、HelpViewerを直接コントロールすると、

AppleScript名:HelpViewerで検索する?(未遂)
tell application “HelpViewer”
  activate
  
search looking for “用語説明”
end tell

★Click Here to Open This Script 

このぐらいの簡素な記述で済むはずですが、結果はHelpViewerに表示されません(単に記述が違っているという可能性もあります)。

これをCocoaの機能を用いて操作すると、きちんと検索結果が表示されます。Webサーバー上にあるヘルプのコンテンツを連続して同一ページを表示させると、5回に1回はエラー(未表示)になるなど、挙動がよくわかりません。

HelpViewerの中身はほぼ(遅い)Webブラウザで、有用性についても疑問が残ります。

AppleScript名:指定アプリケーションのヘルプブック内で指定キーワードを検索する
– Created 2017-04-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4590

set anAppName to “Script Editor”
set aTargString to “用語説明” –”Script Dictionary” in Japanese
set hRes to searchHelpBook(anAppName, aTargString) of me

–指定アプリケーションのヘルプブックで、指定アンカーを表示する
on searchHelpBook(anAppName, aTargString)
  set locBookName to getHelpBook(anAppName) of me
  
if locBookName = false then return false
  
current application’s NSHelpManager’s sharedHelpManager()’s findString:aTargString inBook:locBookName
end searchHelpBook

–指定アプリケーションのヘルプブック名称を取得する
on getHelpBook(anAppName)
  set aWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set appPath to aWorkspace’s fullPathForApplication:anAppName
  
if appPath is equal to missing value then return false
  
  
set locBookName to (current application’s NSBundle’s bundleWithPath:appPath)’s objectForInfoDictionaryKey:“CFBundleHelpBookName”
  
if locBookName is equal to missing value then return false
  
–> “com.apple.ScriptEditor.help”
  
return locBookName
end getHelpBook

★Click Here to Open This Script 

2017/04/17 新刊書籍「iTunes Control」のオンライン販売を開始しました

新刊書籍「iTunes Control」の販売を開始しました。全48ページ、全ページカラーです。

AppleScriptがわからなくても、簡単な記述でアプリケーションの情報をとってきたり、アプリケーションに情報を設定したり、曲を検索して再生させるなどの高度な操作がかんたんに行える「体験」をご提供する本です。

AppleScriptをわかっている人には、よりGUIアプリケーションの操作をわかっていただけるものと思います。

「ホップ」「ステップ」「ジャンプ」の3段階難易度設定で、急に難しくならない「痛くない入門書」。一番最後に掲載されているプログラムリストでも10行に満たないコンパクトさ。それでいて、iTunesをはじめとしたGUIアプリケーション操作の真髄ともいえる、フィルタ参照を実際に体感できます。

なお、初心者向けの「Control」シリーズと、中級者向けの「Scripting」シリーズを計画しており、「iTunes Control」の続刊にはご要望にお応えして「Keynote Control」を予定しています。