Archive for the '10.12 savvy' Category

2017/06/22 指定矩形内に含まれるデータをリストで返す v3

指定矩形座標内に指定座標が含まれるかどうかを判定するAppleScriptです。

NSPointInRectを見つけて、NSRectの中にNSPointが含まれるかどうかチェックできることがわかりました(汗)

→ 矩形座標(NSRect)同士の衝突判定はこちら

AppleScript名:指定矩形内に含まれるデータをリストで返す v3
– Created 2017-06-22 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4702

set aList to {{507, 162}, {338, 166}, {389, 166}, {448, 166}, {286, 110}, {504, 109}, {558, 108}, {341, 113}, {394, 115}, {447, 112}, {234, 107}, {611, 110}, {286, 58}, {501, 63}, {556, 58}, {337, 59}, {391, 60}, {448, 63}, {611, 58}}

set rangeList to {{500, 100}, {550, 180}} –{{x1,y1}, {x2,y2}}
set resList to withinRange(rangeList, aList) of me
–>  {{507, 162}, {504, 109}}

on withinRange(rangeList as list, targetList as list)
  set aRect to makeNSRect(rangeList) of me
  
  
set includedList to {}
  
repeat with i in targetList
    copy i to {x, y}
    
set aPoint to current application’s NSMakePoint(x, y)
    
set aRes to (current application’s NSPointInRect(aPoint, aRect)) as boolean
    
    
if aRes = true then
      set the end of includedList to contents of i
    end if
  end repeat
  
  
return includedList
end withinRange

on makeNSRect(aList as list)
  try
    copy aList to {{x1, y1}, {x2, y2}}
    
set xWidth to (x2 - x1)
    
set yHeight to (y2 - y1)
    
set a1Rect to {origin:{x:x1, y:y1}, |size|:{width:xWidth, height:yHeight}}
    
return a1Rect
  on error
    return missing value
  end try
end makeNSRect

★Click Here to Open This Script 

2017/06/22 指定矩形内に含まれるデータをリストで返す v2

指定矩形座標内に指定座標が含まれるかどうかを判定するAppleScriptです。

以前に掲載したPure AppleScript版から改修してCocoaの機能で書き直してみました。ただ、これで正しいのかどーかというのはいまひとつ確証がありません。

width=0, height=0のRectを作ってオーバーラップのチェックをしてみましたが、これではオーバーラップの検出ができなかったためにwidth=1, height=1にしてみました。

→ やっぱり計算内容に納得が行かなかったので作った改修版(v3)はこちら

AppleScript名:指定矩形内に含まれるデータをリストで返す v2
– Created 2017-06-22 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4701

set aList to {{507, 162}, {338, 166}, {389, 166}, {448, 166}, {286, 110}, {504, 109}, {558, 108}, {341, 113}, {394, 115}, {447, 112}, {234, 107}, {611, 110}, {286, 58}, {501, 63}, {556, 58}, {337, 59}, {391, 60}, {448, 63}, {611, 58}}

set rangeList to {{500, 100}, {550, 180}} –{{x1,y1}, {x2,y2}}
set resList to withinRange(rangeList, aList) of me
–>  {{507, 162}, {504, 109}}

on withinRange(rangeList, targetList)
  set aRect to makeNSRect(rangeList) of me
  
  
set includedList to {}
  
repeat with i in targetList
    set bRect to makeNSRectFromXY(i) of me
    
if bRect = missing value then error “Missing Data”
    
set aRes to detectRectanglesCollision(aRect, bRect) of me
    
if aRes = true then
      set the end of includedList to contents of i
    end if
  end repeat
  
  
return includedList
end withinRange

on makeNSRectFromXY(bList as list)
  try
    copy bList to {x, y}
    
set xWidth to 1
    
set yHeight to 1
    
set a1Rect to {origin:{x:x, y:y}, |size|:{width:xWidth, height:yHeight}}
    
return a1Rect
  on error
    return missing value
  end try
end makeNSRectFromXY

on makeNSRect(aList as list)
  try
    copy aList to {{x1, y1}, {x2, y2}}
    
set xWidth to (x2 - x1)
    
set yHeight to (y2 - y1)
    
set a1Rect to {origin:{x:x1, y:y1}, |size|:{width:xWidth, height:yHeight}}
    
return a1Rect
  on error
    return missing value
  end try
end makeNSRect

–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

★Click Here to Open This Script 

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 日の出、日没時刻を計算する

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

edsunriseset.png
▲もともとのGUI版サンプルプログラム

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

→ Download Binary (26KB)

本Scriptは単にEDSunriseSetのサンプル中に書かれていたサンプルデータをそのまま利用したものであり、本来であればCoreLocationを呼び出して現在位置を取得し、システム設定からタイムゾーン情報を取得し位置情報から逆住所ジオコーダーで場所の住所情報を取得するといった処理になることでしょう。もちろん、任意の年月日の日付オブジェクトを作って指定するとか。

AppleScript名:日の出、日没時刻を計算する
– 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/4696

set cityRecList to {{kCityName:“Madrid”, kCityLatitude:(40.4165), kCityLongitude:(-3.70256), kCityTimeZone:“Europe/Madrid”}, {kCityName:“Beijing”, kCityLatitude:(39.9075), kCityLongitude:(116.39723), kCityTimeZone:“Asia/Shanghai”}, {kCityName:“Cupertino”, kCityLatitude:(37.3229978), kCityLongitude:(-122.0321823), kCityTimeZone:“America/Los_Angeles”}, {kCityName:“New York”, kCityLatitude:(40.7127837), kCityLongitude:(-74.0059413), kCityTimeZone:“America/New_York”}, {kCityName:“Tokyo”, kCityLatitude:(35.6894875), kCityLongitude:(139.6917064), kCityTimeZone:“Asia/Tokyo”}, {kCityName:“Sydney”, kCityLatitude:(-33.8674869), kCityLongitude:(151.2069902), kCityTimeZone:“Australia/Sydney”}}

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月19日月曜日 13:44:22″, sunset:date “2017年6月20日火曜日 4:48:04″, cityname:”Madrid”}, {sunrise:date “2017年6月19日月曜日 5:45:35″, sunset:date “2017年6月19日月曜日 20:45:54″, cityname:”Beijing”}, {sunrise:date “2017年6月19日月曜日 21:47:30″, sunset:date “2017年6月20日火曜日 12:31:43″, cityname:”Cupertino”}, {sunrise:date “2017年6月19日月曜日 18:24:37″, sunset:date “2017年6月20日火曜日 9:30:19″, cityname:”New York”}, {sunrise:date “2017年6月19日月曜日 4:25:14″, sunset:date “2017年6月19日月曜日 18:59:52″, cityname:”Tokyo”}, {sunrise:date “2017年6月19日月曜日 5:59:28″, sunset:date “2017年6月19日月曜日 15:53:30″, cityname:”Sydney”}}

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
  
–>  date “2017年6月19日月曜日 4:25:14″
  
  
set aSunSetDate to (aSunrizeSunset’s sunset) as date
  
–>  date “2017年6月19日月曜日 18:59:52″
  
  
return {sunrise:aSunRiseDate, sunset:aSunSetDate, cityname:kCityName of cityRec}
end getSunriseSunset

★Click Here to Open This Script 

2017/06/18 PDFから本文テキストを抽出して配列にストアして文字列検索

指定PDFで指定キーワードを検索して、キーワードが存在するページのノンブル(数値)のリストを返すAppleScriptの改良強化版です。

最初にPDFからページ単位でテキストを抽出し、テキスト検索キャッシュを作成。このテキスト検索キャッシュに対して検索を実行し、存在しなかったらPDFに対してテキスト検索を行うようにしてみました。

最初からPDFに対してテキスト検索するよりも、テキスト抽出後に検索するほうが、複数キーワードの検索ではスピードが有利になるものと期待しています。

これで不満が出るようなら、AppleScriptで並列処理を行なって処理速度をかせぐしかないでしょう。

AppleScript名:PDFから本文テキストを抽出して配列にストアして文字列検索
– Created 2017-06-18 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/4691

property textCache : missing value
property aList : {}

–検索対象の語群
set sList to {“notification”, “Cocoa”} –considering case

set thePath to POSIX path of (choose file of type {“com.adobe.pdf”})

–PDFのテキスト内容をあらかじめページごとに読み取って、検索用のテキストキャッシュを作成
set anNSURL to (current application’s |NSURL|’s fileURLWithPath:thePath)
set theDoc to current application’s PDFDocument’s alloc()’s initWithURL:anNSURL
set theCount to theDoc’s pageCount() as integer

set textCache to current application’s NSMutableArray’s new()

repeat with i from 0 to (theCount - 1)
  set aPage to (theDoc’s pageAtIndex:i)
  
set tmpStr to (aPage’s |string|())
  (
textCache’s addObject:{pageIndex:i + 1, pageString:tmpStr})
end repeat

–主にテキストキャッシュを対象にキーワード検索
repeat with s in sList
  
  
–❶部分一致で抽出
  
set bRes to ((my filterRecListByLabel1(textCache, “pageString contains ’” & s & “’”))’s pageIndex) as list
  
  
–❷、❶のページ単位のテキスト検索で見つからなかった場合(ページ間でまたがっている場合など)
  
if bRes = {} then
    set bRes to {}
    
set theSels to (theDoc’s findString:s withOptions:0)
    
repeat with aSel in theSels
      set thePage to (aSel’s pages()’s objectAtIndex:0)’s label()
      
set curPage to (thePage as integer)
      
if curPage is not in bRes then
        set the end of bRes to curPage
      end if
    end repeat
  end if
  
  
set the end of aList to bRes
  
end repeat

return aList

–リストに入れたレコードを、指定の属性ラベルの値で抽出
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
  
return filteredArray
end filterRecListByLabel1

★Click Here to Open This Script 

2017/06/17 PDFでテキスト検索してキーワードの存在ページをリストで返す

指定PDFで指定キーワードを検索して、キーワードが存在するページのノンブル(数値)のリストを返すAppleScriptです。

自分の書いた本のPDFファイル(483ページ)で検索を行なってみたところ、数秒程度はかかりました。

この手の処理では、同じScriptを実行しても2回目以降もとくにスピードアップしないので、ページごとに個別にテキスト抽出しておいて、配列に対してテキスト検索するほうが高速処理できると思われます。

配列変数上でページごとに分けておいたテキストに対して検索を行い、見つからなかった場合には仕方なく本ルーチンのような処理でPDFに対してテキスト検索を行うといったところでしょうか。

AppleScript名:PDFでテキスト検索してキーワードの存在ページをリストで返す
– Created 2016-01-05 10:17:51 by Shane Stanley
– Modified 2017-06-17 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Quartz”
–http://piyocast.com/as/archives/4690

set aPath to POSIX path of (choose file of type {“com.adobe.pdf”})
set aSearchKeyword to “数値のインクリメント/デクリメント”
set guardPage to 15 –検索対象から外すページ(冒頭からこのページまでを除外)
set pRes to searchPDFforString(aPath, aString, guardPage) of me
–>  {67, 78}

–指定のPDFの指定のキーワードを検索してキーワードが存在するページのリストを返す
on searchPDFforString(posixPath, aSearchKeyword, guardPage)
  set theURL to current application’s |NSURL|’s fileURLWithPath:posixPath
  
set thePDF to current application’s PDFDocument’s alloc()’s initWithURL:theURL
  
  
set theSels to (thePDF’s findString:searchString withOptions:0)
  
set aList to {}
  
  
repeat with aSel in theSels
    set thePage to (aSel’s pages()’s objectAtIndex:0)’s label()
    
set curPage to (thePage as integer)
    
if curPage > guardPage then
      if curPage is not in aList then
        set the end of aList to curPage
      end if
    end if
  end repeat
  
  
return aList
end searchPDFforString

★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/14 Googleで検索して結果を返す v2

指定キーワードをGoogle検索して該当するWebサイトのURLをリストで返すAppleScriptです。

もともとはShane StanleyがAppleScript Users MLで発表したオリジナル版(v1)があり、これに対して、私が検索件数の拡張(100件まで)を行なったものです(3月の時点でShaneにフィードバックずみ)。

本Scriptは、AppleScriptからCocoaの機能を呼び出す処理を洗練させまくった内容であり、よくこんなもんを考えついて実装したものだと心底感心させられます。いままで見たScriptの中でもダントツに(いろんな意味で)突き抜けています。突き抜けすぎていて、掲載するまで(Googleに刺されないかどうか)かなり悩まされたほどです。

# ほかの言語でも同じような処理をしている例を見つけたので、問題は少ないものと判断

Webブラウザ経由でGoogle検索を行い、結果をAppleScriptで取得するようなやりかたは、短期的には使うことができていましたが、Google側の広告や表示スタイルの変更などによってScriptの定期的な書き換えや修正が必要になっていました。その場で試す分には使えるものの、長期的に使い続けることには疑問が生じていました。

本Scriptでは検索結果のHTMLをXMLとして評価し、Webブラウザ側に表示される各種要素には関係なく検索結果のURLのみを抽出。Webブラウザ経由のGoogle検索の手口を悪魔的に洗練させたものといえます。最大検索件数は100件なので用途は制限されますが、お手軽な検索程度であれば使えます。

本当に業務で長期的にGoogle検索をシステムやツールの一部の機能として利用するのであれば、Google Web API(REST API)経由でGoogle検索エンジンを呼び出すようにすることが望ましいでしょう。AppleScriptからMicrosoftのBing検索エンジンにREST API経由で検索実行することも可能です

AppleScript名:Googleで検索して結果を返す v2
– Created 2017-03-31 by Shane Stanley
– Modified 2017-03-31 by Takaaki Naganoya
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
–http://piyocast.com/as/archives/4687

set theQuery to “AppleScript”
set aResList to searchByGoogle(theQuery, 100) of me

on searchByGoogle(theQuery, maxNum)
  – build and escape query string
  
set theQuery to current application’s NSString’s stringWithString:theQuery
  
set theQuery to theQuery’s stringByReplacingOccurrencesOfString:space withString:“+”
  
set escQuery to theQuery’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())
  
  
– do search
  
set searchURLString to current application’s NSString’s stringWithFormat_(“https://www.google.com/search?client=safari&rls=en&ie=UTF-8&oe=UTF-8&q=%@&complete=0&num=%@&as_qdr=all”, escQuery, (maxNum as string))
  
set theURL to current application’s |NSURL|’s URLWithString:searchURLString
  
set {theData, theError} to current application’s NSData’s dataWithContentsOfURL:theURL options:0 |error|:(reference)
  
if theData = missing value then error (theError’s localizedDescription() as text)
  
  
– convert to XML
  
set {theXMLDoc, theError} to current application’s NSXMLDocument’s alloc()’s initWithData:theData options:(current application’s NSXMLDocumentTidyHTML) |error|:(reference)
  
if theXMLDoc = missing value then error (theError’s localizedDescription() as text)
  
  
– filter via XPath and predicate
  
set xpathStr to “//*[@class=\”r\”]/a/@href”
  
set {theNodes, theError} to theXMLDoc’s nodesForXPath:xpathStr |error|:(reference)
  
if theNodes = missing value then error (theError’s localizedDescription() as text)
  
set nodeStrings to (theNodes’s valueForKey:“stringValue”)
  
set thePred to current application’s NSPredicate’s predicateWithFormat:“self BEGINSWITH ’/url?q=’”
  
set nodeStrings to nodeStrings’s filteredArrayUsingPredicate:thePred
  
  
– trim URL strings
  
set theURLStrings to {}
  
repeat with aString in nodeStrings
    set theOffset to (aString’s rangeOfString:“&”)
    
set end of theURLStrings to (aString’s substringWithRange:{7, (theOffset’s location) - 7}) as text
  end repeat
  
  
return theURLStrings
end searchByGoogle

★Click Here to Open This Script 

2017/06/13 指定PDFの最初のページにアノテーションを追加する(テキストアノテーション)

指定のPDFの最初のページにテキストのアノテーションを追加するAppleScriptです。

だいたい想定していたとおりの処理はできているはずなんですが、Preview.app上で確認してみると想定していたのとは違う(クリックするとテキストが展開される)ので、まだいろいろ試してみないとダメっぽい感じです。

▼処理したPDFをPreview.appでオープンしたところ
pdf_ano1_resized.png

▼本AppleScriptで添付したアノテーションをクリックしたところ
pdf_ano2_resized.png

PDFのアノテーションまわりはmacOS 10.13で大幅に手が加わって変更されるので、このAppleScriptは単なるmacOS 10.10.x〜10.12.x上でのアノテーション追加実験ということになります。

AppleScript名:指定PDFの最初のページにアノテーションを追加する(テキストアノテーション)
– Created 2017-06-13 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “Quartz”
use framework “QuartzCore”
use framework “AppKit”
–http://piyocast.com/as/archives/4685

set aHFSPath to (choose file of type {“com.adobe.pdf”} with prompt “Choose a 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 firstPage to (aPDFdoc’s pageAtIndex:0)

set textAnnotation to current application’s PDFAnnotationText’s alloc()’s initWithBounds:{origin:{x:10, y:400}, |size|:{width:200, height:100}}
textAnnotation’s setType:(current application’s PDFAnnotationTextWidget)
textAnnotation’s setValue:“/FreeText” forAnnotationKey:(current application’s kPDFAnnotationKey_Subtype)
textAnnotation’s setValue:“Hello PDF” forAnnotationKey:(current application’s kPDFAnnotationKey_Contents)
textAnnotation’s setValue:(current application’s NSColor’s yellowColor()) forAnnotationKey:(current application’s kPDFAnnotationKey_Color)

firstPage’s addAnnotation:textAnnotation

aPDFdoc’s writeToFile:aPOSIX

★Click Here to Open This Script 

2017/06/12 PDFでテキスト検索してURLリンクのアノテーションを追加する

指定のPDFで指定のテキスト(list)を検索して、URLリンク(list)のアノテーションを追加するAppleScriptです。

PDFにアノテーションを追加するAppleScriptで、他のアプリケーションを併用せずにQuartz Frameworkの機能を利用するタイプのものを探してみたら、Shane StanleyがMacScripter.netに投稿したものだけが見つかりました。一応、読みやすく清書して一部変更したのと、日本語環境で日本語を含んだPDFに対して処理検証を行なったものを掲載しています。

ただ、Objective-Cで記述したサンプルについてもほとんど見つからないので、なかなか探すのに苦労させられています。むしろ、サードパーティのフレームワーク「PSPDFkit」あたりのほうがサンプルが充実しているので、用途によってはこちらも選択肢に入ってくることでしょう。

macOS 10.13, High SierraでPDFkitに大幅に手が入るようなので、そちらの登場を待てるようであれば、10.13のPDFkitを使ってもよいでしょう。

AppleScript名:PDFでテキスト検索してURLリンクのアノテーションを追加する
– Created 2016-01-05 10:17:51 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Quartz”
–http://piyocast.com/as/archives/4682

set aPath to POSIX path of (choose file of type {“com.adobe.pdf”})
set pRes to my makeLinksInPDF:aPath forStrings:{“日本語 WordNet”, “日本語WordNet”} linkURLs:{“http://compling.hss.ntu.edu.sg/wnja/”, “http://compling.hss.ntu.edu.sg/wnja/”}

–指定のPDFの指定のキーワード群に対してURL群でリンクのアノテーションを追加する
on makeLinksInPDF:posixPath forStrings:listOfSearchStrings linkURLs:listOfLinkURLStrings
  set theURL to current application’s |NSURL|’s fileURLWithPath:posixPath
  
set thePDF to current application’s PDFDocument’s alloc()’s initWithURL:theURL
  
  
repeat with i from 1 to count of listOfSearchStrings
    
    
set searchString to item i of listOfSearchStrings
    
set linkURLString to item i of listOfLinkURLStrings
    
    
– get list of matches as PDFSelections
    
set theSels to (thePDF’s findString:searchString withOptions:0)
    
    
repeat with aSel in theSels
      set thePage to (aSel’s pages()’s objectAtIndex:0)
      
set theBounds to (aSel’s boundsForPage:thePage)
      
      
set theLink to (current application’s PDFAnnotationLink’s alloc()’s initWithBounds:theBounds) – make link with those bounds
      
set theAction to (current application’s PDFActionURL’s alloc()’s initWithURL:(current application’s |NSURL|’s URLWithString:linkURLString))
      
      (
theLink’s setMouseUpAction:theAction)
      
      
– set link’s appearance
      (
theLink’s setColor:(current application’s NSColor’s blueColor()))
      
set linkBorder to current application’s PDFBorder’s alloc()’s init()
      (
linkBorder’s setLineWidth:1.0)
      (
linkBorder’s setStyle:0)
      (
theLink’s setBorder:(linkBorder))
      (
theLink’s setShouldDisplay:true)
      
      
– add it to the page
      (
thePage’s addAnnotation:theLink)
    end repeat
  end repeat
  
  
– save the modified PDF
  
set oldName to theURL’s lastPathComponent()’s stringByDeletingPathExtension()
  
set newURL to (theURL’s URLByDeletingLastPathComponent()’s URLByAppendingPathComponent:(oldName’s stringByAppendingString:“-new”))’s URLByAppendingPathExtension:“pdf”
  
thePDF’s writeToURL:newURL
  
end makeLinksInPDF:forStrings:linkURLs:

★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/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/06/04 指定スクリプト書類の記述OSA言語を取得する

指定スクリプト書類の記述OSA(Open Scripting Architecture)言語の情報を取得するAppleScriptです。

指定のスクリプト書類がどのOSA言語で記述されているかについては、Script Editorでオープンして書類のプロパティを取得するのが手っ取り早い方法ですが、

AppleScript名:Script Editorでスクリプト書類をオープンして記述OSA言語名称を取得
set anAlias to choose file of type {“com.apple.applescript.script”, “com.apple.applescript.script-bundle”}
tell application “Script Editor”
  set aDoc to open anAlias
  
tell aDoc
    set aProp to properties
    
set aLang to name of language of aProp
  end tell
end tell

★Click Here to Open This Script 

これだと、スクリプト書類内で呼び出しているアプリケーションが自動的に起動されます。

スクリプト書類を大量に処理するような場合、アプリケーションが自動起動されたくないような用途もあります。そのために、Cocoaの機能(OSAKit)を呼び出して、スクリプト書類から直接情報を取得してみました。

これだと、InDesignのコントロールを行なっているスクリプトから情報を取得しても、InDesignが起動されることはありません。

AppleScript名:指定スクリプト書類の記述OSA言語を取得する
– Created 2017-06-04 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “OSAKit”
–http://piyocast.com/as/archives/4674

set anAlias to choose file of type {“com.apple.applescript.script”, “com.apple.applescript.script-bundle”}

set aURL to current application’s |NSURL|’s fileURLWithPath:(POSIX path of anAlias)
set theScript to current application’s OSAScript’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)

set scriptName to theScript’s |language|()’s |name|() as string
set scriptDesc to theScript’s |language|()’s info() as string
set scriptVer to theScript’s |language|()’s |version|() as string

return {osaName:scriptName, osaDesc:scriptDesc, osaVer:scriptVer}
–>  {osaName:”AppleScript”, osaDesc:”AppleScript.”, osaVer:”2.5″}
–>  {osaName:”JavaScript”, osaDesc:”JavaScript”, osaVer:”1.1″}

★Click Here to Open This Script 

2017/06/03 指定フォルダ中のPure ASのファイルを別フォルダに移動

指定フォルダ中のPure AppleScriptのファイルを別フォルダに移動するAppleScriptです。

最近は(処理速度が速いことや並列実行させやすいことから)なるべくCocoaの機能を呼び出すASOC(AppleScriptObjC)の部品を使うようにしているので、Pure AppleScript(Cocoaの機能を使わないもの)のファイルを整理して別フォルダに移動させておくためのScriptを書いてみました。

任意のAppleScript書類からソースを取得して、ソース中にuse framework “Foundation”の宣言文が入っていないかどうかを確認しています。入っていればASOC、入っていなければPure ASと判定。実行専用のScriptからはソースコードは取得できないので無視していますし、実行形式のアプレットについては最初の段階で対象外にしています。

AppleScript名:指定フォルダ中のPure ASのファイルを別フォルダに移動
– Created 2017-06-03 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “OSAKit”
–http://piyocast.com/as/archives/4673

set tFol to choose folder with prompt “処理対象フォルダを選択”
set sFol to choose folder with prompt “Pure AppleScriptの移動先フォルダを選択”

tell application “Finder”
  tell folder (tFol)
    set sList to (every file whose name ends with “.scpt” or name ends with “.scptd”) as alias list
  end tell
end tell

repeat with i in sList
  set sRes to detectScriptIsPureASorASOC(i) of me
  
if sRes = false then
    tell application “Finder”
      move i to sFol
    end tell
  end if
end repeat

–指定AppleScriptファイルがPure ASかASOCかを判定して返す
on detectScriptIsPureASorASOC(aFile)
  set sText to getASsourceFor(aFile) of me
  
if sText = “” or sText = missing value then return missing value
  
if sText contains “use framework \”Foundation\”" then
    return true –ASOC (AppleScript with Cocoa Framework)
  else
    return false –Pure AppleScript
  end if
end detectScriptIsPureASorASOC

–指定AppleScriptファイルのソースコードを取得する(実行専用Scriptからは取得できない)
on getASsourceFor(anAlias as {alias, string})
  set anHFSpath to anAlias as string
  
set aURL to current application’s |NSURL|’s fileURLWithPath:(POSIX path of anHFSpath)
  
set theScript to current application’s OSAScript’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
  
return theScript’s source() as text
end getASsourceFor

★Click Here to Open This Script 

2017/05/30 メールアドレスからMessage-IDらしきものを作成

与えられたメールアドレスから、メールヘッダに入るMessage-IDらしきものを生成するAppleScriptです。実行結果は、毎回変わります(そういう目的のために作ったので)。

高速メール送信が行えるWeb API「SendGrid」でメールの高速送信をAppleScriptから行ってみました。1通あたり0.2秒程度。さらに複数一括指定送信を行うと、AppleScriptからでも1通あたり0.007秒ぐらいで送信できたので(100通一括指定時)、たいへんに有用性が高いサービスだと感じられました。

そんなテストを繰り返す中、テスト送信先のメールアドレスがGmailのアドレスで、しかも1つのGMailアドレスに対して「+」でメールアドレス拡張を行ったときに、複数のメールが1つのメールとして見なされるという問題が起こりました。

その対処方法を探してみたところ、Twitter上で「Mail-HeaderにMessage-IDを入れるといいよ」と教えてもらいました。

Message-IDの指定について調べてみると、SendGrid呼び出し時にMessage-IDフィールドにメールアドレスをそのまま突っ込むといった乱暴なサンプルが見つかりました。とりあえず1回送るだけならこれでも問題がなさそうですが、ちゃんとユニークな値のMessage IDを入れないと、同じメールアドレスに複数回メールを送ったときに問題が発生する可能性がありそうでした。

厳密にRFC5322に準拠していなくても、それっぽくて一意に区別できれば問題ないものと考え、それっぽいものを作るAppleScriptを用意してみました。一応、MailCoreのプロジェクトの中にMessage-IDを作るメソッドがないかさがしてみたものの、単体で呼び出せるようなメソッドが見つからなかったので、まーこんなもんでしょう。

AppleScript名:メールアドレスからMessage-IDらしきものを作成
– Created 2017-05-30 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4672

set anAddress to “piyomarusoft+0001@gmail.com”
set anID to getMessageIDFromMailAddress(anAddress) of me
–>  ”<D6A128D3-2D53-4163-B2F7-53A25FB9608C@gmail.com>”

–とりあえずMessage IDらしきものを組み立ててみた
on getMessageIDFromMailAddress(anAddress as string)
  set anOffset to (offset of “@” in anAddress)
  
if anOffset = 0 then return false –Error
  
set aDomain to text anOffset thru -1 of anAddress
  
set aStr to (current application’s NSUUID’s UUID()’s UUIDString()) as string
  
return (“< " & aStr & aDomain & “>”)
end getMessageIDFromMailAddress

–offset命令の実行を横取りする
on offset of searchStr in str
  set aRes to getOffset(str, searchStr) of me
  
return aRes
end offset

on getOffset(str, searchStr)
  set d to divideBy(str, searchStr)
  
if (count d) is less than 2 then return 0
  
return (length of item 1 of d) + 1
end getOffset

on divideBy(str, separator)
  set delSave to AppleScript’s text item delimiters
  
set the AppleScript’s text item delimiters to separator
  
set strItems to every text item of str
  
set the AppleScript’s text item delimiters to delSave
  
return strItems
end divideBy

★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/25 AppleScript+Web APIで行う自然言語処理

ライター/編集者仲間である友人と雑談していたところ、自然言語処理がいろいろ使えそうなシーンが見えてきました。彼によると、Twitter上で過去記事の紹介ツイートを行いたいが、何も関係ないところに唐突に記事紹介のツイートを投げても不自然だし、興味を持たれにくいので、時事ネタと関連する記事を紹介する仕組みを作れないだろうか、という話になりました。

そこで、比較的鮮度および注目度が高いと思われる「ニュース記事」から固有名詞だけを抽出し、その固有名詞にどのぐらい世間的な注目が集まっているかを判定できると、そのキーワードに関連する過去記事をツイートしやすくなるかもしれないと考えました。

apitoreのREST APIには、ニュースフィードを提供するもの、形態素解析を行うもの、Twitter検索を行うものなどがあり、これらを組み合わせるとできそうな感じがしてきました。

→ Playing with rest api

実際に試してみたところ、ニュースフィードからニュース記事本文を抽出するのは(RSS Feedに本文の冒頭部分が含まれていないケースもあるので、結局記事のサイトを見に行って本文を抽出する必要がある)けっこう大変で、タイトル(題名)のみ処理することに。

次に、タイトルの文章を形態素解析して、「固有名詞」だけを抽出してみました。これは別に難しくもなんともありません。さらに、入り組んだrecordから目的のデータを取り出すのは、AppleScriptでもCocoaの機能を使えば造作もありません。NSDictionaryからvalueForKeyPath:でオブジェクトパスを記述して抽出すれば、データ取り出しのために長々とループ処理で記述する必要はありません。

次の段階は、その時点における「注目度」の計算です。仮にTwitter全体を1つの「世間」とみなし、Twitter上で拡散されていたりお気に入りに入れられる回数の高い単語は、比較的「注目度」が高いものだろうという仮説を立てました。

→ キーワードの言語ごとのTwitter発言内容集計

そこで、ニュースのタイトルから抽出した固有名詞をTwitterで検索し、それぞれの固有名詞がTweet上でどの程度リツイートされたか、お気に入りに入れられたかを計算してみました。これについては、コストの問題からREST APIではなくローカルのmacOS上で動作するAppleScript用Twitterクライアント「TwitterScripter」を呼び出すことにしました。apitore上の各種REST APIは無料で試用できますが、呼び出し回数が増えれば料金がかかるため、その部分を節約することが目的です。TwitterScripterを使用することによるデメリットは、それ自体が並列処理に対応していないこと。Mac上で(AppleScriptによる)並列処理を行ってTwitter検索を行う場合には、直接Twitterの(割と整理されていない)APIを直接呼び出すか、apitore上のREST APIを呼び出すしかなさそうです。

AS.Parallel

指定のキーワード(固有名詞)を含むツイートをTwitter上で検索し、それぞれがどの程度リツイートされたか、お気に入りに入れられたかを実際に数値化。その数値をもとにキーワードのランキングを計算することができました。

一応、「活性度の高い」≒「注目度の高い」キーワードを計算できたわけです。ここまでのプログラムはすでに実稼働状態にあり、「こんな(へんな)単語が注目されてるのかー!」という未知の単語のピックアップに成功しています。

ただし、これでは目的を50%しか達成できていません。

この「注目度の高いキーワード」と「過去記事」の間をブリッジする必要があるからです。もう少しわかりやすくいえば、「カール」という本日注目度急上昇中のキーワードがありますが、この「カール」という固有名詞で過去記事を検索したところで、まったく異なる単語の一部がたまたま偶然ひっかかってヒットする程度で、過去記事が超絶的にヒットしにくいことが予想されます(グスタフ・カールとかザンスカール帝国とかカール自走臼砲とかパリダカールラリーとか)。

この「カール」を「スナック菓子」という上位概念に変換し、さらにスナック菓子に所属する単語を取得して、それらの単語で検索できるとよいでしょう(菓子、和菓子など)。このあたりは個人的に「スター・クエリー」と昔から呼んでいるもので、与えられたキーワードから類似・関連するキーワードを複数生成して検索することで、ヒットする確率を高める仕組みです。GoogleやYouTubeでもおそらく類似の機構が検索エンジンに実装されており、入力した単語以外でもヒットするようになっているようです(とくにYouTube)。

apitoreでも日本語Word.netのデータを利用して類義語や上位概念の言葉を計算できるようになっていますが、日本語の基本的な語句に特化しており、実際に試してみたところ「カール」「ガンダム」といった商品名などから類義語や上位概念を求める用途に向いていないことがわかりました。これらの計算を行うためには、Wikipediaのデータからカテゴリを取得できれば、やりやすくなるものと思われます。

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/21 与えられた数が素数かどうかチェック v2

与えられた数が素数かどうか(素数テーブルを使わずに)チェックするAppleScriptの改良版です。

C言語などの他の言語での実装を確認していたら、「あれ、ループ範囲がチェック値の半分になってるぞ」と気づき、理由に思い当たって修正したものです。

よくよく考えてみたら、素数というのは「1とそれ以外で割ることのできない数」なわけですが、その範囲で最小の素数が2なので、素数チェックの範囲は「2から(その数-1)」ではなく、「2から(その数÷2)」の間でチェックすればよいことに。当初行っていたループ処理の半分の時間で素数チェックが行えます。

AppleScript名:与えられた数が素数かどうかチェック v2
– Created 2017-05-21 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
–http://piyocast.com/as/archives/4659

set pRes to checkPrimeNumber(9973) of me
–> true

set pRes to checkPrimeNumber(24) of me
–> false

set pRes to checkPrimeNumber(1021) of me
–> true

set pRes to checkPrimeNumber(311) of me
–> true

–素数チェック
on checkPrimeNumber(aNum as integer)
  if aNum < 1 then error “Number is smaller than 1.”
  
repeat with i from 2 to (aNum div 2)
    if aNum mod i is equal to 0 then
      return false
    end if
  end repeat
  
return true
end checkPrimeNumber

★Click Here to Open This Script 

2017/05/20 与えられた数が素数かどうかチェック(PrimeNumberHelper)

オープンソースのprime-number-objc(By NazarYavornytskyy)中のPrimeNumberHelperをFramework化した「primeNumKit」を呼び出して指定の数が素数かどうかをチェックするAppleScriptです。

また、「primeNumKit」を利用した素因数分解のScriptも掲載しておきます。

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

–> Download Framework Binary

素数のチェックで対象の数が大きく、かつ素数である場合には処理に時間がかかってしまいます。一方で、チェック対象の数がそれほど大きくない場合には、AppleScriptだけでループ処理した方がObjective-Cのプログラムを呼び出すよりも高速なケースが多々あります。

そこで、場合分けしてAppleScriptとObjective-Cのプログラムを選択的に呼び出すようにすると演算性能を維持できるようです。応用範囲が広いため、素数のチェックは割と重要です。

AppleScript名:与えられた数が素数かどうかチェック(PrimeNumberHelper)
– Created 2017-05-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “primeNumKit”
–https://github.com/NazarYavornytskyy/prime-number-objc
–http://piyocast.com/as/archives/4656

set aPrime to current application’s PrimeNumberHelper’s alloc()’s init()
set a to 75777773
set aRes to (aPrime’s isPrime:a) as boolean

★Click Here to Open This Script 

AppleScript名:簡単な素因数分解v4
– Created 2017-05-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “primeNumKit”
–https://github.com/NazarYavornytskyy/prime-number-objc
–http://piyocast.com/as/archives/4656

set aRes to getPrimeFactor(56) of me
–> {2, 3, 3}

set aRes to getPrimeFactor(4999999) of me
–> {2, 127}

set aRes to getPrimeFactor(9999991) of me
–> {1021} –素数

–与えられた数を素因数分解する
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)
  if aNum < 10000 then –0.25秒以上かかりそうな処理はCocoaで処理(実測値ベース)
    return checkPrimeNumberSub1(aNum) of me
  else
    set aPrime to current application’s PrimeNumberHelper’s alloc()’s init()
    
return (aPrime’s isPrime:aNum) as boolean
  end if
end checkPrimeNumber

–小さい数の場合の素数検出
on checkPrimeNumberSub1(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 checkPrimeNumberSub1

–素因数チェック
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

★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/19 FileMaker Pro 16でセキュリティ機能が追加される

先日、FileMaker Proの最新バージョンv16が発表され、FileMakerからお試し版がダウンロードできるようになりました。

手元のデータベースを試してみたところ、メニューから(AppleScriptを呼び出している)FileMaker Scriptを実行したら実行権限エラーに。

display dialog程度のコマンドが書いてあるAppleScriptでは実行権限エラーにはなりません。正確に書けば、

「FileMaker Proを操作するAppleScriptがFileMakerスクリプトステップ中に書かれていた場合には実行権限エラーになる」

という状態です。ただし、これは仕様にもとづく動作であり、バグではありません。

FileMaker Pro 16では、アクセス権セットの「拡張アクセス権」で、詳細なシステムへのアクセス機能の許可/禁止が行えるようになったようです。

FileMaker Script中でAppleScriptを実行していると、「Apple EventおよびActive-XによるFileMaker操作の実行を許可」の項目にひっかかってAppleScriptの実行が遮断されるとのこと。

FileMaker Script中におけるAppleScriptの実行を許可するためには、この、

  「Apple EventおよびActive-XによるFileMaker操作の実行を許可」

にチェックを入れておく必要があります。

私が気づいていなかっただけで、より以前のFileMaker Proで追加されていた機能である可能性もあります。

2017/05/18 16進数文字列を10進数数値に変換するv2

16進数文字列をCocoaの機能を利用して10進数数値に変換するAppleScriptです。

以前、Pure AppleScriptで組んだものがありましたが、ためしにCocoaの機能を利用して実行速度を比較してみました。

 Pure AppleScript版:0.03秒ぐらい
 本AppleScript:0.02秒ぐらい

Cocoaの機能を呼び出すAppleScriptObjCのScriptは、Pure AppleScriptにくらべて1回目の実行がやや遅くなるものの、同じ処理を複数回呼び出すと2回目以降の速度が大幅に上がる傾向があります。繰り返し実行する場合にはAppleScriptObjC版のほうがおすすめです。

AppleScript名:16進数文字列を10進数数値に変換するv2
– Created 2017-05-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4645

set aHex to “0×9AC”
set hNum to retIntFromHexString(aHex) of me

on retIntFromHexString(aHex as string)
  set {aRes, hexRes} to (current application’s NSScanner’s scannerWithString:aHex)’s scanHexInt:(reference)
  
if aRes = false then
    return “” –エラーの場合
  else
    return hexRes
  end if
end retIntFromHexString

★Click Here to Open This Script 

2017/05/18 簡単な素因数分解v3

簡単な素因数分解を行うAppleScriptです。

これまで、素数のチェックを行う手間を省いて素数リストをデータに持って素因数分解を行ったりはしていましたが、素数のチェック自体はそれほど難しくないことに気づきまして(汗)

  「素数=1とそれ自体以外で割り切れない数」

ということで、普通に2から対象の数-1までループして、途中で割り切れたら素数ではない、というだけでチェックが可能でした。

素数のチェックさえ計算できてしまえば、応用で素因数分解も簡単なので、すぐに素因数分解までできました。

本格的に使うのであれば、素数計算の結果を「計算キャッシュ」としてファイルにキャッシュしておき、同じ数の素数判定演算を2回目以降は行わないようにするとよさそうです。ループで計算を行うコスト(時間)と、計算キャッシュの内容を取り出すコスト(時間)を実際に計測して釣り合いが取れるかどうか確認する必要はあるでしょう。

ループして割り切れないかどうかチェックしているだけなので、チェック対象の数値が大きくなると純粋に処理時間が伸びてしまいます。75777773の素数チェックを行ってみたら、本Scriptでは35秒。Objective-Cの素数チェックルーチンをFramework化して呼び出してみたら0.23秒でした。キャッシュするよりも、Objective-CのFrameworkを呼び出してみたほうが速度的にメリットが大きいかもしれません。

ただ、チェック対象の数が小さいと、Framework呼び出しの方が遅くなる傾向がありました。複数の手段をミックスして、チェック対象の数の大きさで場合分けするのがいいのかも。

AppleScript名:与えられた数が素数かどうかチェック
– Created 2017-05-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
–http://piyocast.com/as/archives/4644

set pRes to checkPrimeNumber(24) of me
–> false

set pRes to checkPrimeNumber(1021) of me
–> true

set pRes to checkPrimeNumber(311) of me
–> true

–素数チェック
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

★Click Here to Open This Script 

AppleScript名:簡単な素因数分解v3
– Created 2017-05-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
–http://piyocast.com/as/archives/4644

set aRes to getPrimeFactor(18) of me
–> {2, 3, 3}

set aRes to getPrimeFactor(254) of me
–> {2, 127}

set aRes to getPrimeFactor(1021) of me
–> {1021} –素数

–与えられた数を素因数分解する
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

★Click Here to Open This Script