Archive for the 'Application Control' Category

2018/01/19 なろう系ルビタグを置換

「小説家になろう」サイトからダウンロードしたテキストファイルに入っている、ルビのタグを置換するAppleScriptです。

音声読み上げを行うために、ルビタグの内容を残してルビをつけている対象のキーワードを削除してみました。また、テキストから言語の推定を行うのに、ルビタグのような特殊なタグが入っていると(日本語とわかっていながらも)、正しく言語を推定できないという問題もありました。

この手の処理ではNSScannerが重宝しますが、「タグのあった場所から前方にさかのぼって、文字種類が変わったところまでを削除」、という処理はさすがに地道にループで処理しないとダメそうだったので、そのようにしています。

「テキスト全文に対して形態素解析を行なって、各単語のRangeを取得し、ルビタグとRangeが隣接しているものを処理対象にする」

といった処理も考えてみましたが、隣接Rangeの検出のメソッドが見当たらなかったのと(オーバーラップしている場合は検出できるんですが)、処理内容がほとんどいまのプログラムと変わらないので、お蔵入りに。

現在のプログラムで、21,546文字の日本語のテキストを置換して自分のマシン(MacBook Pro Retina 2012)で0.27秒でした。

実際に使ってみて、TTS(テキスト音声読み上げ)サービスで読ませてみましたが、漢字の読み方を作品ごとによみがなに展開しないとしっくりこない(例:「西方諸国」→TTSだと「さいほうしょこく」。実際には「せいほうしょこく」)ので、そのあたりも置換しないといけないようです。

AppleScript名:なろう系ルビタグを置換
– Created 2018-01-14 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/5117

property NSString : a reference to current application’s NSString
property NSScanner : a reference to current application’s NSScanner
property NSMutableArray : a reference to current application’s NSMutableArray
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch
property NSNumberFormatterRoundUp : a reference to current application’s NSNumberFormatterRoundUp

set aStr to “ 数多国ある西方諸国だが、元を辿ればとある一つの国へとつながっていた。それは幻晶騎士(シルエットナイト)の力により西方の地に覇をとなえた人類が作り上げた超巨大国家、その名を“ファダーアバーデン”という。
 西方暦一二八九年の現在において西方諸国を構成する主要国家、“ジャロウデク王国”、“クシェペルカ王国”、“ロカール諸国連合”、“|孤独なる十一《イレブンフラッグス》”などの国々は、全てかの巨大国家が分裂してできた残滓なのである。”

–set aStr to getEditorText()

–”|○o○o○《XXXXX》” –> “XXXXX”
set bRes to trimStrHeaderFromTo(aStr, “|”, “《”, “》”) of me

–”aaaaa○○○(XXXXX)” –> “XXXXX”
set cStr to trimStrHeaderFromToForward(bRes, “(”, “)”) of me

(*
“ 数多国ある西方諸国だが、元を辿ればとある一つの国へとつながっていた。それはシルエットナイトの力により西方の地に覇をとなえた人類が作り上げた超巨大国家、その名を“ファダーアバーデン”という。
 西方暦一二八九年の現在において西方諸国を構成する主要国家、“ジャロウデク王国”、“クシェペルカ王国”、“ロカール諸国連合”、“イレブンフラッグス”などの国々は、全てかの巨大国家が分裂してできた残滓なのである。”
*)

–”|○o○o○《XXXXX》” –> “XXXXX”
on trimStrHeaderFromTo(aParamStr, headerStr, fromStr, toStr)
  set theScanner to NSScanner’s scannerWithString:aParamStr
  
set anArray to NSMutableArray’s array()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {theResult, theKey} to theScanner’s scanUpToString:headerStr intoString:(reference)
    
    
theScanner’s scanString:fromStr intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
if theValue is missing value then set theValue to “”
    
    
theScanner’s scanString:fromStr intoString:(missing value)
    
    
anArray’s addObject:theValue
  end repeat
  
  
if anArray’s |count|() = 0 then return aParamStr
  
  
copy aParamStr to curStr
  
repeat with i in (anArray as list)
    set curStr to repChar(curStr, i & fromStr, “”) of me
  end repeat
  
  
set curStr to repChar(curStr, toStr, “”) of me
  
  
return curStr
end trimStrHeaderFromTo

–”aaaaa○○○(XXXXX)” –> “XXXXX”
on trimStrHeaderFromToForward(aParamStr, fromStr, toStr)
  set theScanner to NSScanner’s scannerWithString:aParamStr
  
set anArray to NSMutableArray’s array()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {theResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
set curLoc to (theScanner’s scanLocation()) + 1
    
    
–scan back to different kind of character
    
set prevKind to detectCharKindMain(text (curLoc - 1) of aParamStr) of me
    
    
repeat with i from curLoc - 2 to 1 by -1
      set aStr to text i of aParamStr
      
set curKind to detectCharKindMain(aStr) of me
      
      
if prevKind is not equal to curKind then
        exit repeat
      end if
    end repeat
    
    
try
      set tmpStr to text (i + 1) thru curLoc of aParamStr
      
theScanner’s scanString:fromStr intoString:(missing value)
      
set {theResult, theValue} to theScanner’s scanUpToString:fromStr intoString:(reference)
      
if theValue is missing value then set theValue to “”
      
      
theScanner’s scanString:fromStr intoString:(missing value)
      
      
anArray’s addObject:tmpStr
    end try
    
  end repeat
  
  
if anArray’s |count|() = 0 then return aParamStr
  
  
copy aParamStr to curStr
  
repeat with i in (anArray as list)
    set curStr to repChar(curStr, i, “”) of me
  end repeat
  
  
set curStr to repChar(curStr, toStr, “”) of me
  
  
return curStr
end trimStrHeaderFromToForward

on repChar(aStr, targStr, repStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:targStr withString:repStr
  
set cString to bString as string
  
return cString
end repChar

–文字種別判定
on detectCharKindMain(aStr)
  set s1Res to chkKanji(aStr) of me
  
set s2Res to chkKatakana(aStr) of me
  
set s3Res to chkHiragana(aStr) of me
  
set s4Res to chkLineFeed(aStr) of me
  
set s5Res to chkSpecialSign(aStr) of me
  
set s6Res to chkAlphaNumeric(aStr)
  
  
if s1Res = true then
    set curKind to “Kanji”
  else if s2Res = true then
    set curKind to “Katakana”
  else if s3Res = true then
    set curKind to “Hiragana”
  else if s4Res = true then
    set curKind to “Line Feed”
  else if s5Res = true then
    set curKind to “Sign”
  else if s6Res = true then
    set curKind to “Alpha Numeric”
  end if
  
  
return curKind
end detectCharKindMain

on chkKanji(aChar)
  return detectCharKind(aChar, “[一-龠]”) of me
end chkKanji

on chkHiragana(aChar)
  return detectCharKind(aChar, “[ぁ-ん]”) of me
end chkHiragana

on chkKatakana(aChar)
  return detectCharKind(aChar, “[ァ-ヶ]”) of me
end chkKatakana

on chkLineFeed(aChar)
  return aChar is in {string id 10, string id 13, string id 13 & string id 10}
end chkLineFeed

on chkSpecialSign(aChar)
  return aChar is in {“「”, “」”, “『”, “』”, “ー”, “―”, “〜”, “〜”, “!”, “?”, “&”, “/”, “《”, “》”, “#”, “…”, “・”, “♪”, “。”, “、”, “.”, “々”, ““”, “””, “*”, “(”, “)”, “(”, “)”, ” “, “ ”, “§”, “【”, “】”, “■”, “%”, “≒”}
end chkSpecialSign

on chkAlphaNumeric(aChar)
  return detectCharKind(aChar, “[a-zA-Z0-9a-zA-Z0-9]”) of me –半角全角英数字
end chkAlphaNumeric

on detectCharKind(aChar, aPattern)
  set aChar to NSString’s stringWithString:aChar
  
set searchStr to NSString’s stringWithString:aPattern
  
set matchRes to aChar’s rangeOfString:searchStr options:(NSRegularExpressionSearch)
  
if matchRes’s location() = (current application’s NSNotFound) or (matchRes’s location() as number) > 9.99999999E+8 then
    return false
  else
    return true
  end if
end detectCharKind

on getEditorText()
  tell application “CotEditor”
    if (count every document) = 0 then return false
    
tell front document
      return contents
    end tell
  end tell
end getEditorText

★Click Here to Open This Script 

2018/01/18 Numbersでセルのカラム幅を自動調整 v2

Numbersでオープン中の書類の表示中のシート内にある複数の表のカラム幅を自動調整するAppleScriptです。

numb1_resized.png
▲実行前

numb2_resized.png
▲実行後

カラム幅の自動調整を行う対象は、1つの表だけでも、複数の表が存在していても大丈夫ですが、カラム数は同じ必要があります。

一番上に存在する表1の幅を基準に、一番左のカラムは固定のままで、残りのカラムの幅を均等幅に調整します。

複数の表が存在している場合には、すべて表1のとおりにカラム幅を調整します。

AppleScript名:Numbersでセルのカラム幅を自動調整 v2
– Created 2018-1-11 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/5116

tell application "Numbers"
  tell front document
    tell sheet 1
      set tCount to count every table
      
if tCount = 0 then return
      
      
tell table 1
        set tableWidth to width
        
        
set cCount to count every column
        
set cWidth to width of every column
        
set aWidth to width –table width
        
        
set aveWidth to (aWidth - (first item of cWidth)) / (cCount - 1)
        
        
tell columns 2 thru cCount
          set width to aveWidth
        end tell
        
        
set wList to width of every column
      end tell
      
      
if tCount = 1 then return
      
      
set origCols to count every column of table 1
      
repeat with i from 2 to tCount
        if origCols is not equal to (count every column of table i) then
          display notification "Numbers: Column number is different in Table #" & (i as string)
          
return
        end if
      end repeat
      
      
repeat with i from 2 to tCount
        
        
tell table i
          set width to tableWidth
        end tell
        
        
set aCount to 1
        
        
tell table i
          repeat with ii from 1 to cCount
            tell column ii
              set width to (contents of item ii of wList)
            end tell
          end repeat
        end tell
        
      end repeat
    end tell
  end tell
end tell

★Click Here to Open This Script 

2018/01/18 テキストをTTSで読み上げて所要時間を算出 v2.1(CotEditor版)

CotEditorでオープン中の最前面の書類の本文テキストを取得し、macOS標準搭載のText To Speechの機能を用いて最大限に遅い速度(180 words per minute)と速い速度(220 words per minute)の2通りで読み上げて音声読み上げ所要時間のシミュレーション結果をCotEditor内蔵のコンソールに出力するAppleScriptです。

実際の読み上げ時間よりもはるかに短い時間でファイルへの音声レンダリングを実行し、生成した音声ファイルの再生時間(Duration)を取得することで、TTS読み上げ所要時間≒人間が実際に読み上げたときの所要時間を求めます。また、シミュレーション中は音声をファイルに出力するため、スピーカーなどから音声を出すわけではありません。

ttsreadout.png

CotEditor内蔵のScript Menuに入れて実行できることを確認しました。当然、OS側のScript Menuに入れて実行できます。

6,200文字の日本語テキストの読み上げ実時間の計測(速いパターンと遅いパターンの2回実行)に30秒ほどかかりました。読み上げ実時間は15分ぐらいだったので、実時間よりは速く処理できますが、割と時間がかかる処理です。

また、これは「言うは易し、行うは・・・」の典型例のような処理でした。sayコマンドによるTTS読み上げ、音声のファイル出力、読み上げ実時間を待たずにサウンドファイル出力などの機能がありながら、これらが非同期で実行されるために骨が折れます。

出力ファイルの再生時間を取得しようとするとエラー、出力ファイルパスのPOSIX pathにquoted form of でクォート処理するとエラー、などなど。

とりあえず、sayコマンドによる音声ファイル出力が終わったあと、音声ファイルが本当に出力終了するまでファイルをチェックしつつ待ちます。sayコマンドからの出力が並列処理で行えれば、並列処理向きの内容だと思います。

CotEditor以外でも実際にAppleScriptに対応しているエディタであれば、本文テキストを取得するだけなのでテキストエディットをはじめだいたいのものには対応できます。

AppleScript名:テキストをTTSで読み上げて所要時間を算出 v2.1(CotEditor版)
– Created 2018-01-10 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AVFoundation”
use framework “AppKit”
–http://piyocast.com/as/archives/5113

property |NSURL| : a reference to current application’s |NSURL|
property NSDate : a reference to current application’s NSDate
property NSUUID : a reference to current application’s NSUUID
property NSFileManager : a reference to current application’s NSFileManager
property AVAudioPlayer : a reference to current application’s AVAudioPlayer
property NSDateFormatter : a reference to current application’s NSDateFormatter
property NSSpeechSynthesizer : a reference to current application’s NSSpeechSynthesizer

set str3 to getEditorText() of me
if str3 = false then return

set aVoice to “Kyoko”

–Check existence of TTS Voice name
set vList to retAvailableTTSnames() of me
if aVoice is not in vList then error “Wrong TTS Voice Name”

set d1 to readTextByTTSVoiceAndReturnDuration(str3, aVoice, 180) of me –aSpeedRate is “Words per minute. 180 to 220″
set d2 to readTextByTTSVoiceAndReturnDuration(str3, aVoice, 220) of me

set outStr to (formatHMS(d1) of me & “/180 words per minute”) & return & (formatHMS(d2) of me & “/220 words per minute”) & return
tell application “CotEditor”
  activate
  
write to console outStr
end tell

on readTextByTTSVoiceAndReturnDuration(aStr as string, aVoice as string, aSpeedRate as integer)
  set aUUID to NSUUID’s UUID()’s UUIDString() as string
  
–set aPath to (((path to temporary items from user domain) as string) & aUUID & “.aif”)
  
set aPath to (((path to desktop) as string) & aUUID & “.aif”)
  
set aPOSIX to POSIX path of aPath
  
  
tell current application
    say aStr using aVoice saving to (aPOSIX) speaking rate aSpeedRate without waiting until completion
  end tell
  
  
repeat 100000 times
    set aExt to NSFileManager’s defaultManager()’s fileExistsAtPath:aPOSIX
    
if aExt as boolean = true then exit repeat
    
delay “0.001″ as real
  end repeat
  
  
if aExt = false then error “No Sound file”
  
  
set aDur to getDuration(aPath as alias) of me
  
try
    do shell script “rm -f “ & quoted form of POSIX path of aPath
  end try
  
  
return aDur as real
end readTextByTTSVoiceAndReturnDuration

on getDuration(aFile)
  set aURL to |NSURL|’s fileURLWithPath:(POSIX path of aFile)
  
  
repeat 1000 times
    set aAudioPlayer to AVAudioPlayer’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
    
set aRes to aAudioPlayer’s prepareToPlay()
    
if aRes as boolean = true then exit repeat
    
delay 0.5
  end repeat
  
if aRes = false then error “TTS sound output failed”
  
  
set channelCount to aAudioPlayer’s numberOfChannels()
  
set aDuration to aAudioPlayer’s duration()
  
return aDuration as real
end getDuration

on retAvailableTTSnames()
  set outList to {}
  
  
set aList to NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aInfo to (NSSpeechSynthesizer’s attributesForVoice:j)
    
set aInfoRec to aInfo as record
    
set aName to VoiceName of aInfoRec
    
set the end of outList to aName
  end repeat
  
  
return outList
end retAvailableTTSnames

on formatHMS(aTime)
  set aDate to NSDate’s dateWithTimeIntervalSince1970:aTime
  
set aFormatter to NSDateFormatter’s alloc()’s init()
  
  
—This formatter text is localized in Japanese.
  
if aTime < hours then
    aFormatter’s setDateFormat:“mm分ss秒”
  else if aTime < days then
    aFormatter’s setDateFormat:“HH時間mm分ss秒”
  else
    aFormatter’s setDateFormat:“DD日HH時間mm分ss秒”
  end if
  
  
set timeStr to (aFormatter’s stringFromDate:aDate) as string
  
return timeStr
end formatHMS

on getEditorText()
  tell application “CotEditor”
    if (count every document) = 0 then return false
    
tell front document
      return contents
    end tell
  end tell
end getEditorText

★Click Here to Open This Script 

2018/01/17 文字種別にカウントする v2.1(Jedit Ω版)

Jedit Ωの最前面の書類を文字種類別にカウントして結果を新規Jedit Ωドキュメントに出力するAppleScriptです。

jeditomega1.png

Jedit Ω内蔵Script Menuに入れて実行してみたら実行できませんでした。スクリプト書類でもスクリプトバンドルでも無理でした(ASOCの実行自体ができないわけではない)。OS側のScript Menuに入れて実行してください。

Jedit Ωでfront documentの本文テキストを取り出すのに若干苦労しました。なんでこんなことに、、、、

同じテキストをオープンしてみて、Jedit ΩとCotEditorで得られる文字数が若干違うことに気づいてしまいましたが、これっていったいなんでしょうか(^ー^;;

AppleScript名:文字種別にカウントする v2.1(JeditΩ版)
– Created 2018-1-11 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/5111

property NSString : a reference to current application’s NSString
property NSNumber : a reference to current application’s NSNumber
property NSDictionary : a reference to current application’s NSDictionary
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSNumberFormatter : a reference to current application’s NSNumberFormatter
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch
property NSNumberFormatterRoundUp : a reference to current application’s NSNumberFormatterRoundUp

set aRes to getEditorText() of me
if aRes = false then return

set myPath to retFrontDocsPath() of me
if myPath = missing value then return

set aRec to detectCharKindRatio(aRes) of me
set bRes to retFormattedStr(aRec) of me
makeNewDocument of me given parameter:(myPath & return & return & bRes)

on detectCharKindRatio(aStr as string)
  set theCountedSet to NSCountedSet’s alloc()’s initWithArray:(characters of aStr)
  
set theEnumerator to theCountedSet’s objectEnumerator()
  
  
set cCount to 0
  
set hCount to 0
  
set kCount to 0
  
set oCount to 0
  
set lfCount to 0
  
set scCount to 0
  
set anCount to 0
  
  
set totalC to length of aStr
  
set otherList to {}
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    
set aStr to aValue as string
    
set tmpCount to (theCountedSet’s countForObject:aValue)
    
    
set s1Res to chkKanji(aStr) of me
    
set s2Res to chkKatakana(aStr) of me
    
set s3Res to chkHiragana(aStr) of me
    
set s4Res to chkLineFeed(aStr) of me
    
set s5Res to chkSpecialSign(aStr) of me
    
set s6Res to chkAlphaNumeric(aStr)
    
    
if s1Res = true then
      set cCount to cCount + tmpCount
    else if s2Res = true then
      set kCount to kCount + tmpCount
    else if s3Res = true then
      set hCount to hCount + tmpCount
    else if s4Res = true then
      set lfCount to lfCount + tmpCount
    else if s5Res = true then
      set scCount to scCount + tmpCount
    else if s6Res = true then
      set anCount to anCount + tmpCount
    else
      set oCount to oCount + tmpCount
      
set the end of otherList to aStr
    end if
  end repeat
  
  
set ckRes to roundingUp((cCount / totalC) * 100, 1) of me
  
set kkRes to roundingUp((kCount / totalC) * 100, 1) of me
  
set hgRes to roundingUp((hCount / totalC) * 100, 1) of me
  
set otRes to roundingUp((oCount / totalC) * 100, 1) of me
  
set lfRes to roundingUp((lfCount / totalC) * 100, 1) of me
  
set scRes to roundingUp((scCount / totalC) * 100, 1) of me
  
set anRes to roundingUp((anCount / totalC) * 100, 1) of me
  
  
return {kanjiNum:cCount, kanjiRatio:ckRes, hiraganaNum:hCount, hiraganaRatio:hgRes, katakanaNum:kCount, katakanaRatio:kkRes, otherNum:oCount, otherRatio:otRes, otherContent:otherList, lineFeedCount:lfCount, lineFeedRatio:lfRes, specialSignNum:scCount, specialSignRatio:scRes, alphaNumericNum:anCount, alphaNumericRatio:anRes, totalCount:totalC}
end detectCharKindRatio

on chkKanji(aChar)
  return detectCharKind(aChar, “[一-龠]”) of me
end chkKanji

on chkHiragana(aChar)
  return detectCharKind(aChar, “[ぁ-ん]”) of me
end chkHiragana

on chkKatakana(aChar)
  return detectCharKind(aChar, “[ァ-ヶ]”) of me
end chkKatakana

on chkLineFeed(aChar)
  return aChar is in {string id 10, string id 13, string id 13 & string id 10}
end chkLineFeed

on chkSpecialSign(aChar)
  return aChar is in {“「”, “」”, “『”, “』”, “ー”, “―”, “〜”, “〜”, “!”, “?”, “&”, “/”, “《”, “》”, “#”, “…”, “・”, “♪”, “。”, “、”, “.”, “々”, ““”, “””, “*”, “(”, “)”, “(”, “)”, ” “, “ ”, “§”, “【”, “】”, “■”, “%”, “≒”}
end chkSpecialSign

on chkAlphaNumeric(aChar)
  return detectCharKind(aChar, “[a-zA-Z0-9a-zA-Z0-9]”) of me –半角全角英数字
end chkAlphaNumeric

on detectCharKind(aChar, aPattern)
  set aChar to NSString’s stringWithString:aChar
  
set searchStr to NSString’s stringWithString:aPattern
  
set matchRes to aChar’s rangeOfString:searchStr options:(NSRegularExpressionSearch)
  
if matchRes’s location() = (current application’s NSNotFound) or (matchRes’s location() as number) > 9.99999999E+8 then
    return false
  else
    return true
  end if
end detectCharKind

on roundingUp(aNum, aDigit as integer)
  set a to aNum as real
  
set aFormatter to NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setMaximumFractionDigits:aDigit
  
aFormatter’s setRoundingMode:(NSNumberFormatterRoundUp)
  
set aStr to aFormatter’s stringFromNumber:(NSNumber’s numberWithFloat:a)
  
return (aStr as text) as real
end roundingUp

on getEditorText()
  tell application id “jp.co.artman21.JeditOmega”
    if (count every document) = 0 then return false
    
tell front document
      return text of it
    end tell
  end tell
end getEditorText

–処理結果から出力データの組み立て
on retFormattedStr(aRec)
  set a to “漢字  :” & (repChar(“■”, ((kanjiRatio of aRec) div 10)) of me & “  “ & (kanjiRatio of aRec) as string) & “%” & return
  
set b to “ひらがな:” & (repChar(“■”, ((hiraganaRatio of aRec) div 10)) of me & “  “ & (hiraganaRatio of aRec) as string) & “%” & return
  
set c to “カタカナ:” & (repChar(“■”, ((katakanaRatio of aRec) div 10)) of me & “  “ & (katakanaRatio of aRec) as string) & “%” & return
  
set d to “改行文字:” & (repChar(“■”, ((lineFeedRatio of aRec) div 10)) of me & “  “ & (lineFeedRatio of aRec) as string) & “%” & return
  
set e to “特殊記号:” & (repChar(“■”, ((specialSignRatio of aRec) div 10)) of me & “  “ & (specialSignRatio of aRec) as string) & “%” & return
  
set f to “英数字 :” & (repChar(“■”, ((alphaNumericRatio of aRec) div 10)) of me & “  “ & (alphaNumericRatio of aRec) as string) & “%” & return & return
  
set g to (“総文字数:” & encodeJapaneseNumText(totalCount of aRec) & ” 文字。 400字詰め原稿用紙で約 “ & ((totalCount of aRec) div 400) as string) & ” 枚分” & return
  
set all to a & b & c & d & e & f & g
  
return all
end retFormattedStr

on repChar(aChar, aTimes)
  set outStr to “”
  
repeat aTimes times
    set outStr to outStr & aChar
  end repeat
  
return outStr
end repChar

–数字文字列を日本語数値表現文字列に変換
on encodeJapaneseNumText(aText as string)
  set dotText to “.” as string
  
set upperDigit to “”
  
set lowerDigit to “”
  
  
–小数点の処理
  
if dotText is in aText then
    set b to offset of dotText in aText
    
set upperDigit to characters 1 thru (b - 1) of aText
    
set upperDigit to upperDigit as string
    
set lowerDigit to characters b thru -1 of aText
    
set lowerDigit to lowerDigit as string
  else
    set upperDigit to aText
  end if
  
  
set scaleList3 to {“”, “万”, “億”, “兆”, “京”, “垓”, “丈”, “壌”, “溝”, “砂”, “正”, “載”, “極”, “恒河沙”, “阿僧梢”, “那由他”, “不可思議”, “無量大数”}
  
set splitDigit to 4
  
set nList to splitByDigit(upperDigit, splitDigit) of me
  
set nList to reverse of nList
  
  
set resText to “”
  
set digCount to 1
  
repeat with i in nList
    set b to (contents of i) as number
    
if b is not equal to 0 then
      set resText to (b as text) & item digCount of scaleList3 & resText
    end if
    
set digCount to digCount + 1
  end repeat
  
  
return resText & lowerDigit
  
end encodeJapaneseNumText

–指定桁数で区切る
on splitByDigit(a, splitDigit)
  set aList to characters of a
  
set aList to reverse of aList
  
  
set resList to {}
  
set tempT to “”
  
set tempC to 1
  
repeat with i in aList
    set tempT to contents of i & tempT
    
if tempC mod splitDigit = 0 then
      set resList to {tempT} & resList
      
set tempT to “”
    end if
    
set tempC to tempC + 1
  end repeat
  
  
if tempT is not equal to “” then
    set resList to {tempT} & resList
  end if
  
  
resList
  
end splitByDigit

on makeNewDocument given parameter:aStr
  tell application id “jp.co.artman21.JeditOmega”
    set aDoc to make new document with properties {text:aStr}
  end tell
end makeNewDocument

on retFrontDocsPath()
  tell application id “jp.co.artman21.JeditOmega”
    tell front document
      return path
    end tell
  end tell
end retFrontDocsPath

★Click Here to Open This Script 

2018/01/16 文字種別にカウントする v2.1(CotEditor版)

CotEditorの最前面の書類を文字種類別にカウントして結果を新規CotEditorドキュメントに出力するAppleScriptです。

cot2.png
▲結果出力

cot1.png
▲セキュリティ上の制約の大きいCotEditor内蔵のScript Menuからも実行可能

改行記号や英数字、特殊文字のカウントを追加してみました。改行文字の多すぎ/少なすぎを既存の文章から分析できるとよいと思います。「坊ちゃん」の改行文字0.6%はさすがにいま読むとちょっと少なすぎですね。

ほかにも、400字詰原稿用紙で何枚分に相当するかの計算もつけてみました。総文字数のカウント表示には、きちんと日本語数値表現エンコーダライブラリの機能を突っ込んでみました(無量大数まで対応)。

他のテキストエディタ用に転用するのもさほど難しくありません(たいした機能は使っていないので)。

ここにさらに「AppleScriptらしい」ひとひねりを加えるとしたら、音声読み上げしたときの所要時間を読み上げ速度別に計算してレポートするぐらいでしょうか。

AppleScript名:文字種別にカウントする v2.1
– Created 2018-1-11 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/5109

property NSString : a reference to current application’s NSString
property NSNumber : a reference to current application’s NSNumber
property NSDictionary : a reference to current application’s NSDictionary
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSNumberFormatter : a reference to current application’s NSNumberFormatter
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch
property NSNumberFormatterRoundUp : a reference to current application’s NSNumberFormatterRoundUp

set aRes to getCotEditorText() of me
if aRes = false then return

set myPath to retFrontDocsPath() of me
if myPath = missing value then return

set aRec to detectCharKindRatio(aRes) of me
set bRes to retFormattedStr(aRec) of me
makeNewDocument of me given parameter:(myPath & return & return & bRes)

on detectCharKindRatio(aStr as string)
  set theCountedSet to NSCountedSet’s alloc()’s initWithArray:(characters of aStr)
  
set theEnumerator to theCountedSet’s objectEnumerator()
  
  
set cCount to 0
  
set hCount to 0
  
set kCount to 0
  
set oCount to 0
  
set lfCount to 0
  
set scCount to 0
  
set anCount to 0
  
  
set totalC to length of aStr
  
set otherList to {}
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    
set aStr to aValue as string
    
set tmpCount to (theCountedSet’s countForObject:aValue)
    
    
set s1Res to chkKanji(aStr) of me
    
set s2Res to chkKatakana(aStr) of me
    
set s3Res to chkHiragana(aStr) of me
    
set s4Res to chkLineFeed(aStr) of me
    
set s5Res to chkSpecialSign(aStr) of me
    
set s6Res to chkAlphaNumeric(aStr)
    
    
if s1Res = true then
      set cCount to cCount + tmpCount
    else if s2Res = true then
      set kCount to kCount + tmpCount
    else if s3Res = true then
      set hCount to hCount + tmpCount
    else if s4Res = true then
      set lfCount to lfCount + tmpCount
    else if s5Res = true then
      set scCount to scCount + tmpCount
    else if s6Res = true then
      set anCount to anCount + tmpCount
    else
      set oCount to oCount + tmpCount
      
set the end of otherList to aStr
    end if
  end repeat
  
  
set ckRes to roundingUp((cCount / totalC) * 100, 1) of me
  
set kkRes to roundingUp((kCount / totalC) * 100, 1) of me
  
set hgRes to roundingUp((hCount / totalC) * 100, 1) of me
  
set otRes to roundingUp((oCount / totalC) * 100, 1) of me
  
set lfRes to roundingUp((lfCount / totalC) * 100, 1) of me
  
set scRes to roundingUp((scCount / totalC) * 100, 1) of me
  
set anRes to roundingUp((anCount / totalC) * 100, 1) of me
  
  
return {kanjiNum:cCount, kanjiRatio:ckRes, hiraganaNum:hCount, hiraganaRatio:hgRes, katakanaNum:kCount, katakanaRatio:kkRes, otherNum:oCount, otherRatio:otRes, otherContent:otherList, lineFeedCount:lfCount, lineFeedRatio:lfRes, specialSignNum:scCount, specialSignRatio:scRes, alphaNumericNum:anCount, alphaNumericRatio:anRes, totalCount:totalC}
end detectCharKindRatio

on chkKanji(aChar)
  return detectCharKind(aChar, “[一-龠]”) of me
end chkKanji

on chkHiragana(aChar)
  return detectCharKind(aChar, “[ぁ-ん]”) of me
end chkHiragana

on chkKatakana(aChar)
  return detectCharKind(aChar, “[ァ-ヶ]”) of me
end chkKatakana

on chkLineFeed(aChar)
  return aChar is in {string id 10, string id 13, string id 13 & string id 10}
end chkLineFeed

on chkSpecialSign(aChar)
  return aChar is in {“「”, “」”, “『”, “』”, “ー”, “―”, “〜”, “〜”, “!”, “?”, “&”, “/”, “《”, “》”, “#”, “…”, “・”, “♪”, “。”, “、”, “.”, “々”, ““”, “””, “*”, “(”, “)”, “(”, “)”, ” “, “ ”, “§”, “【”, “】”, “■”, “%”, “≒”}
end chkSpecialSign

on chkAlphaNumeric(aChar)
  return detectCharKind(aChar, “[a-zA-Z0-9a-zA-Z0-9]”) of me –半角全角英数字
end chkAlphaNumeric

on detectCharKind(aChar, aPattern)
  set aChar to NSString’s stringWithString:aChar
  
set searchStr to NSString’s stringWithString:aPattern
  
set matchRes to aChar’s rangeOfString:searchStr options:(NSRegularExpressionSearch)
  
if matchRes’s location() = (current application’s NSNotFound) or (matchRes’s location() as number) > 9.99999999E+8 then
    return false
  else
    return true
  end if
end detectCharKind

on roundingUp(aNum, aDigit as integer)
  set a to aNum as real
  
set aFormatter to NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setMaximumFractionDigits:aDigit
  
aFormatter’s setRoundingMode:(NSNumberFormatterRoundUp)
  
set aStr to aFormatter’s stringFromNumber:(NSNumber’s numberWithFloat:a)
  
return (aStr as text) as real
end roundingUp

on getCotEditorText()
  tell application “CotEditor”
    if (count every document) = 0 then return false
    
tell front document
      set aText to contents
    end tell
  end tell
end getCotEditorText

–処理結果から出力データの組み立て
on retFormattedStr(aRec)
  set a to “漢字  :” & (repChar(“■”, ((kanjiRatio of aRec) div 10)) of me & “  “ & (kanjiRatio of aRec) as string) & “%” & return
  
set b to “ひらがな:” & (repChar(“■”, ((hiraganaRatio of aRec) div 10)) of me & “  “ & (hiraganaRatio of aRec) as string) & “%” & return
  
set c to “カタカナ:” & (repChar(“■”, ((katakanaRatio of aRec) div 10)) of me & “  “ & (katakanaRatio of aRec) as string) & “%” & return
  
set d to “改行文字:” & (repChar(“■”, ((lineFeedRatio of aRec) div 10)) of me & “  “ & (lineFeedRatio of aRec) as string) & “%” & return
  
set e to “特殊記号:” & (repChar(“■”, ((specialSignRatio of aRec) div 10)) of me & “  “ & (specialSignRatio of aRec) as string) & “%” & return
  
set f to “英数字 :” & (repChar(“■”, ((alphaNumericRatio of aRec) div 10)) of me & “  “ & (alphaNumericRatio of aRec) as string) & “%” & return & return
  
set g to (“総文字数:” & encodeJapaneseNumText(totalCount of aRec) & ” 文字。 400字詰め原稿用紙で約 “ & ((totalCount of aRec) div 400) as string) & ” 枚分” & return
  
set all to a & b & c & d & e & f & g
  
return all
end retFormattedStr

on repChar(aChar, aTimes)
  set outStr to “”
  
repeat aTimes times
    set outStr to outStr & aChar
  end repeat
  
return outStr
end repChar

–数字文字列を日本語数値表現文字列に変換
on encodeJapaneseNumText(aText as string)
  set dotText to “.” as string
  
set upperDigit to “”
  
set lowerDigit to “”
  
  
–小数点の処理
  
if dotText is in aText then
    set b to offset of dotText in aText
    
set upperDigit to characters 1 thru (b - 1) of aText
    
set upperDigit to upperDigit as string
    
set lowerDigit to characters b thru -1 of aText
    
set lowerDigit to lowerDigit as string
  else
    set upperDigit to aText
  end if
  
  
set scaleList3 to {“”, “万”, “億”, “兆”, “京”, “垓”, “丈”, “壌”, “溝”, “砂”, “正”, “載”, “極”, “恒河沙”, “阿僧梢”, “那由他”, “不可思議”, “無量大数”}
  
set splitDigit to 4
  
set nList to splitByDigit(upperDigit, splitDigit) of me
  
set nList to reverse of nList
  
  
set resText to “”
  
set digCount to 1
  
repeat with i in nList
    set b to (contents of i) as number
    
if b is not equal to 0 then
      set resText to (b as text) & item digCount of scaleList3 & resText
    end if
    
set digCount to digCount + 1
  end repeat
  
  
return resText & lowerDigit
  
end encodeJapaneseNumText

–指定桁数で区切る
on splitByDigit(a, splitDigit)
  set aList to characters of a
  
set aList to reverse of aList
  
  
set resList to {}
  
set tempT to “”
  
set tempC to 1
  
repeat with i in aList
    set tempT to contents of i & tempT
    
if tempC mod splitDigit = 0 then
      set resList to {tempT} & resList
      
set tempT to “”
    end if
    
set tempC to tempC + 1
  end repeat
  
  
if tempT is not equal to “” then
    set resList to {tempT} & resList
  end if
  
  
resList
  
end splitByDigit

on makeNewDocument given parameter:aStr
  tell application id “com.coteditor.CotEditor”
    activate
    
set newDoc to make new document
    
tell newDoc
      set contents to aStr
    end tell
  end tell
end makeNewDocument

on retFrontDocsPath()
  tell application id “com.coteditor.CotEditor”
    tell front document
      return path
    end tell
  end tell
end retFrontDocsPath

★Click Here to Open This Script 

2018/01/16 文字種別ごとにカウントする v2

指定のプレーンテキスト(UTF-8)を漢字、ひらがな、カタカナ、その他で個別に集計するAppleScriptです。

charkind1_resized.png

charkind2_resized.png

文学作品(夏目漱石の「坊ちゃん」)と小説家になろうサイトのラノベで比較してみたところ、カタカナの使用率であからさまに傾向が異なりました。1つの文章の文字数や改行コードの混入率などを調べてもあきらかに傾向が出ると思います。

文字種別の判定を正規表現で処理していますが、すべての文字をシーケンシャルにチェックすると時間がかかるので、NSCountedSetを用いて文字ごとに使用頻度の集計を行なってからループで合算。これで処理時間の短縮を行なっています。テキストの分量が増えると、テキストの文字列ごとの分割(characters of)が遅くなるので注意が必要です。

文章を書くときには、漢字の使用頻度が高くなりすぎないように気をつけているので、こういう集計はできないと困ります。OS側にそういうサービスがあってもよさそうなものですが、残念ながら日本語以外でこういうことに注意が必要な言語が少なさそうなので、標準搭載はされないことでしょう。

macOS 10.12.x〜macOS 10.13.0までの間で存在している「NSNotFoundの値の定義がまちがっているバグ」に対処してあります。

AppleScript名:文字種別にカウントする v2
– Created 2018-1-11 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/5104

property NSString : a reference to current application’s NSString
property NSNumber : a reference to current application’s NSNumber
property NSDictionary : a reference to current application’s NSDictionary
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSNumberFormatter : a reference to current application’s NSNumberFormatter
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch
property NSNumberFormatterRoundUp : a reference to current application’s NSNumberFormatterRoundUp

set sourcePath to choose file of type {“public.plain-text”}
tell current application
  set aStr to (read sourcePath as «class utf8»)
end tell

set aRes to detectCharKindRating(aStr) of me
–> {kanjiNum:563289, kanjiRating:22.0, hiraganaNum:1311933, hiraganaRating:51.2, katakanaNum:210161, katakanaRating:8.2, otherNum:478690, otherRating:18.7, totalCount:2564073}–Light Novel
–> {kanjiNum:24960, kanjiRating:28.0, hiraganaNum:56080, hiraganaRating:62.9, katakanaNum:1063, katakanaRating:1.2, otherNum:7136, otherRating:8.0} –文学(坊ちゃん)

on detectCharKindRating(aStr as string)
  set theCountedSet to NSCountedSet’s alloc()’s initWithArray:(characters of aStr)
  
set theEnumerator to theCountedSet’s objectEnumerator()
  
  
set cCount to 0
  
set hCount to 0
  
set kCount to 0
  
set oCount to 0
  
set totalC to length of aStr
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    
set aStr to aValue as string
    
set tmpCount to (theCountedSet’s countForObject:aValue)
    
    
set s1Res to chkKanji(aStr) of me
    
set s2Res to chkKatakana(aStr) of me
    
set s3Res to chkHiragana(aStr) of me
    
    
if s1Res = true then
      set cCount to cCount + tmpCount
    else if s2Res = true then
      set kCount to kCount + tmpCount
    else if s3Res = true then
      set hCount to hCount + tmpCount
    else
      set oCount to oCount + tmpCount
    end if
  end repeat
  
  
set ckRes to roundingUp((cCount / totalC) * 100, 1) of me
  
set kkRes to roundingUp((kCount / totalC) * 100, 1) of me
  
set hgRes to roundingUp((hCount / totalC) * 100, 1) of me
  
set otRes to roundingUp((oCount / totalC) * 100, 1) of me
  
  
return {kanjiNum:cCount, kanjiRating:ckRes, hiraganaNum:hCount, hiraganaRating:hgRes, katakanaNum:kCount, katakanaRating:kkRes, otherNum:oCount, otherRating:otRes}
end detectCharKindRating

on chkKanji(aChar)
  return detectCharKind(aChar, “[一-龠]”) of me
end chkKanji

on chkHiragana(aChar)
  return detectCharKind(aChar, “[ぁ-ん]”) of me
end chkHiragana

on chkKatakana(aChar)
  return detectCharKind(aChar, “[ァ-ヶ]”) of me
end chkKatakana

on detectCharKind(aChar, aPattern)
  set aChar to NSString’s stringWithString:aChar
  
set searchStr to NSString’s stringWithString:aPattern
  
set matchRes to aChar’s rangeOfString:searchStr options:(NSRegularExpressionSearch)
  
if matchRes’s location() = (current application’s NSNotFound) or (matchRes’s location() as number) > 9.99999999E+8 then
    return false
  else
    return true
  end if
end detectCharKind

on roundingUp(aNum, aDigit as integer)
  set a to aNum as real
  
set aFormatter to NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setMaximumFractionDigits:aDigit
  
aFormatter’s setRoundingMode:(NSNumberFormatterRoundUp)
  
set aStr to aFormatter’s stringFromNumber:(NSNumber’s numberWithFloat:a)
  
return (aStr as text) as real
end roundingUp

★Click Here to Open This Script 

2018/01/13 DBColorNamesで色の名称を取得する

任意のNSColorの色名称を948色のテーブルから取得するオープンソースの「DBColorNames」(By Daniel Beard)をフレームワーク化したdbColNamesKitを呼び出して色の詳細な名称を取得するAppleScriptです。

実行には、dbColNamesKit.frameworkのインストールを必要とします。アーカイブをダウンロードして展開し、~/Library/Frameworksフォルダに入れてください(あくまで自己責任で)。

–> Download Framework Binary

だいたいざっくり「緑色ぐらい」といった判別ではなく、詳細な色見本帳のような判定を行います。性能云々というよりは、処理の方向性が違うといったところでしょうか。

AppleScript名:dbColNamesKitで色の名称を取得する
– Created 2018-01-11 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “dbColNamesKit” –https://github.com/daniel-beard/DBColorNames/
–http://piyocast.com/as/archives/5098

property NSColor : a reference to current application’s NSColor

set {rVal, gVal, bVal} to choose color
set aColor to makeNSColorFromRGBAval(rVal, gVal, bVal, 65535, 65535) of me
set aCDB to current application’s DBColorNames’s alloc()’s init()

set aColorStr to (aCDB’s nameForColor:aColor) as string

on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
  set aRedCocoa to (redValue / aMaxVal) as real
  
set aGreenCocoa to (greenValue / aMaxVal) as real
  
set aBlueCocoa to (blueValue / aMaxVal) as real
  
set aAlphaCocoa to (alphaValue / aMaxVal) as real
  
set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
  
return aColor
end makeNSColorFromRGBAval

★Click Here to Open This Script 

2018/01/13 TextEditで本文色をポスタライズ v1

TextEdit書類の本文色をポスタライズするAppleScriptです。

複数のユーザーでRTFに対して色をつけてチェックを行なったような場合に、指定色が微妙にゆらいでしまっても色のゆらぎを吸収するためのものです。

TextEditでオープン中の最前面のRTF書類中の文字色のゆらぎを吸収します。

オープンソースの「Colours」(By Ben Gordon)をフレームワーク化したcolorsKitを呼び出して、RGB→HSBA変換を行なっています。2D List中のリスト項目検索のためにShane StanleyのBridge Plus AppleScript Librariesを利用しています。

実行には、Bridge PlusおよびcolorsKit.frameworkのインストールを必要とします。colorsKit.frameworkについてはアーカイブをダウンロードして展開し、~/Library/Frameworksフォルダに入れてください(あくまで自己責任で)。

–> Download Framework Binary

te1png.png
▲処理前(緑色の文字の色にゆらぎがある)

t22.png
▲処理後(文字色が12色程度の原色に統一される)

AppleScript名:TextEditで本文色をポスタライズ v1
– Created 2018-01-08 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “colorsKit” –https://github.com/bennyguitar/Colours
use bPlus : script “BridgePlus” –https://www.macosxautomation.com/applescript/apps/BridgePlus.html
–http://piyocast.com/as/archives/5097

property NSColor : a reference to current application’s NSColor
property NSArray : a reference to current application’s NSArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor

script spd
  property colList : {}
  
property attList : {}
end script

load framework

set cList to retTextEditColors() of me
set dList to {}

repeat with i in cList
  copy i to {rVal, gVal, bVal}
  
set newColList to posterizeColor(rVal, gVal, bVal, 65535) of me
  
repTextEditColor(i, newColList) of me
end repeat

on repTextEditColor(targColor, newColor)
  set hitIndex to {}
  
  
tell application “TextEdit”
    tell text of front document
      set colList to color of every attribute run
    end tell
  end tell
  
  
set hitIndex to (current application’s SMSForder’s indexesOfItem:targColor inArray:(colList) inverting:false) as list
  
  
tell application “TextEdit”
    tell text of front document
      repeat with i in hitIndex
        ignoring application responses
          set color of attribute run (i + 1) to newColor –0 based index to 1 based index conversion
        end ignoring
      end repeat
    end tell
  end tell
end repTextEditColor

on posterizeColor(rCol as integer, gCol as integer, bCol as integer, aColorMax)
  set aRes to makeNSColorFromRGBAval(rCol, gCol, bCol, aColorMax, aColorMax) of me
  
set aDic to aRes’s hsbaDictionary()
  
  
set hueVal to (|HSBA-h| of aDic) as real
  
set satVal to (|HSBA-s| of aDic) as real
  
set brightVal to (|HSBA-b| of aDic) as real
  
set alphaVal to (|HSBA-a| of aDic) as real
  
  
if satVal 0.01 then set satVal to 0.0
  
  
if satVal = 0.0 then
    if brightVal 0.2 then
      set colVal to {0, 0, 0} –Black
    else if (brightVal > 0.95) then
      set colVal to {65535, 65535, 65535} –White
    else
      set colVal to {32768, 32768, 32768} –Gray
    end if
  else
    if hueVal (15.0 / 360) or hueVal (330 / 360) then
      set colVal to {65535, 0, 0} –red
    else if hueVal (45.0 / 360) then
      set colVal to {65535, 32768, 0} –orange
    else if hueVal < (70.0 / 360) then
      set colVal to {65533, 63639, 2654} –yellow
    else if hueVal < (150.0 / 360) then
      set colVal to {4626, 35488, 17789} –green
    else if hueVal < (190.0 / 360) then
      set colVal to {0, 60802, 65535} –cyan, light blue
    else if (hueVal < 250.0 / 360.0) then
      set colVal to {0, 0, 65535} –blue
    else if (hueVal < 290.0 / 360.0) then
      set colVal to {32768, 0, 32768} –purple
    else
      set colVal to {65535, 0, 65535} –magenta, pink
    end if
  end if
  
  
return colVal
end posterizeColor

on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
  set aRedCocoa to (redValue / aMaxVal) as real
  
set aGreenCocoa to (greenValue / aMaxVal) as real
  
set aBlueCocoa to (blueValue / aMaxVal) as real
  
set aAlphaCocoa to (alphaValue / aMaxVal) as real
  
set aCocoaList to {aRedCocoa, aGreenCocoa, aBlueCocoa, aAlphaCocoa}
  
set aColor to NSColor’s colorFromRGBAArray:aCocoaList
  
return aColor
end makeNSColorFromRGBAval

on retTextEditColors()
  tell application “TextEdit”
    set dCount to count every document
    
if dCount = 0 then return
    
tell text of front document
      set aList to color of every character
    end tell
  end tell
  
  
set ap1List to uniquify1DList(aList, true) of me
  
set cList to {}
  
repeat with i in ap1List
    set the end of cList to contents of i
  end repeat
  
  
return cList
end retTextEditColors

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

★Click Here to Open This Script 

2018/01/11 RGB色をHSBAに変換して色名称を計算

オープンソースの「Colours」(By Ben Gordon)をフレームワーク化したcolorsKitを呼び出して、指定色が「だいたいどんな色」かを計算するAppleScriptの別バージョンです。

hsba_color_picker.png

自分のやり方(色差ΔEで代表的な色との距離を計算する)のほかにもないものかと調べてみたら、RGB色をHSBA色に変換して色判定を行うものを見つけました。ロジックがとても簡潔で技術的な難易度もおっそろしく低かったので、Objective-Cで書かれたプログラムをAppleScriptに置き換えて検証してみました。

RGB色をHSBAに変換して、Hue(色相)の角度を細分化して「この数値からこの数値まではこの色」といったセグメント分けを行なっています。

実行には、colorsKit.frameworkのインストールを必要とします。アーカイブをダウンロードして展開し、~/Library/Frameworksフォルダに入れてください(あくまで自己責任で)。

–> Download Framework Binary

とてもよくわかるのが、やっぱりこのプログラムでもWhite〜Gray〜Blackの判定を最初に行なっていること。また、LightとDarkの判定も行なっています。

色のセグメント分けの箇所が自分のプログラムとは違いはするものの、とてもよく分かる内容でした。ちなみに、さらに細分化した色名称を取得するものがgithub上で公開されています

AppleScript名:RGB色をHSBAに変換して色名称を計算
– Created 2018-01-08 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “colorsKit” –https://github.com/bennyguitar/Colours
–http://piyocast.com/as/archives/5094

property NSColor : a reference to current application’s NSColor

set {rCol, gCol, bCol} to choose color
set cName to retColorName(rCol, gCol, bCol) of me

on retColorName(rCol as integer, gCol as integer, bCol as integer)
  set aRes to makeNSColorFromRGBAval(rCol, gCol, bCol, 65535, 65535) of me
  
set aDic to aRes’s hsbaDictionary()
  
  
set hueVal to (|HSBA-h| of aDic) as real
  
set satVal to (|HSBA-s| of aDic) as real
  
set brightVal to (|HSBA-b| of aDic) as real
  
set alphaVal to (|HSBA-a| of aDic) as real
  
  
if satVal 0.01 then set satVal to 0.0
  
  
set colName to “”
  
  
if satVal = 0.0 then
    if brightVal 0.2 then
      set colName to “black”
    else if (brightVal > 0.95) then
      set colName to “white”
    else
      set colName to “gray”
      
      
if (brightVal < 0.33) then
        set colorName to “dark “ & colorName
      else if (brightVal > 0.66) then
        set colorName to “light “ & colorName
      end if
      
    end if
  else
    if hueVal (15.0 / 360) or hueVal (330 / 360) then
      set colName to “red”
    else if hueVal (45.0 / 360) then
      set colName to “orange”
    else if hueVal < (70.0 / 360) then
      set colName to “yellow”
    else if hueVal < (150.0 / 360) then
      set colName to “green”
    else if hueVal < (190.0 / 360) then
      set colName to “cyan”
    else if (hueVal < 250.0 / 360.0) then
      set colName to “blue”
    else if (hueVal < 290.0 / 360.0) then
      set colName to “purple”
    else
      set colName to “magenta”
    end if
    
    
if (brightVal < 0.5) then
      set colName to “dark “ & colName
    else if (brightVal > 0.0) then
      set colName to “light “ & colName
    end if
  end if
  
  
return colName
end retColorName

on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
  set aRedCocoa to (redValue / aMaxVal) as real
  
set aGreenCocoa to (greenValue / aMaxVal) as real
  
set aBlueCocoa to (blueValue / aMaxVal) as real
  
set aAlphaCocoa to (alphaValue / aMaxVal) as real
  
set aCocoaList to {aRedCocoa, aGreenCocoa, aBlueCocoa, aAlphaCocoa}
  
set aColor to NSColor’s colorFromRGBAArray:aCocoaList
  
return aColor
end makeNSColorFromRGBAval

★Click Here to Open This Script 

2018/01/09 Dictionaryのキー名称の置換

Dictionaryのキー名称の置換を行うAppleScriptです。

さまざまなWeb APIやCocoaのメソッドからNSMutableDictionary形式でデータを受け取った場合に、キーの名称にAppleScriptのrecordで扱えない文字列(記号、空白、数字で始まる文字列などなど)が入っていると、recordにcastしたあとでデータが取り出しにくくなります(NSDictionaryの状態のときにvalueForKeyPath:でデータを取り出せばいいんですが)。

そこで、NSMutableDictionaryの状態でキー名称を置換してしまえば、recordにcastしても値が取り出せるようになります。そういう用途のために作ったものです。

キー名称の置換リストを、

  {{”キー名称1″, “新規キー名称1″}, {”キー名称2″, “新規キー名称2″}}

のように作成して呼び出します。

一応、キー値の重複がないかはチェックしていますが、ただ単に2D Listを1D Listに変換して重複項目がないか調べているだけなので、もう少し気の利いたチェックも考えられなくもありません。

AppleScript名:Dictionaryのキー名称の置換
– Created 2018-01-07 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/5092

property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableDictionary : a reference to current application’s NSMutableDictionary

set aDicLabels to {“HSBA-s”, “HSBA-b”, “HSBA-a”, “HSBA-h”}
set aDicVals to {0.922195169646, 0.902540624142, 1.0, 0.566439336687}

set aDic to NSMutableDictionary’s dictionaryWithObjects:aDicVals forKeys:aDicLabels

set repList to {{“HSBA-s”, “s”}, {“HSBA-b”, “b”}, {“HSBA-a”, “a”}, {“HSBA-h”, “h”}}
set bRecord to repDictionaryKeys(aDic, repList) of me as record
–>  {s:0.922195169646, b:0.902540624142, a:1.0, h:0.566439336687}

on repDictionaryKeys(aDic, repList)
  set rep2List to FlattenList(repList) of me
  
set rep3 to returnDuplicatesOnly(rep2List) of me
  
if rep3 is not equal to {} then error “Duplicate Keys”
  
  
repeat with i in repList
    copy i to {origKey, newKey}
    
set tmpVal to (aDic’s valueForKey:origKey)
    (
aDic’s setObject:tmpVal forKey:newKey)
    (
aDic’s removeObjectForKey:origKey)
  end repeat
  
  
return aDic
end repDictionaryKeys

–By Paul Berkowitz
on FlattenList(aList)
  set oldDelims to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {“????”}
  
set aString to aList as text
  
set aList to text items of aString
  
set AppleScript’s text item delimiters to oldDelims
  
return aList
end FlattenList

on returnDuplicatesOnly(aList as list)
  set aSet to NSCountedSet’s alloc()’s initWithArray:aList
  
set bList to (aSet’s allObjects()) as list
  
  
set dupList to {}
  
repeat with i in bList
    set aRes to (aSet’s countForObject:i)
    
if aRes > 1 then
      set the end of dupList to (contents of i)
    end if
  end repeat
  
  
return dupList
end returnDuplicatesOnly

★Click Here to Open This Script 

2018/01/05 Coloursで指定色がだいたいどんな色なのか推測

オープンソースの「Colours」(By Ben Gordon)をフレームワーク化したcolorsKitを呼び出して、指定色が「だいたいどんな色」かを計算するAppleScriptです。

実行には、colorsKit.frameworkのインストールを必要とします。アーカイブをダウンロードして展開し、~/Library/Frameworksフォルダに入れてください(あくまで自己責任で)。

NSColorでも色のRGBデータの配列でもなんでもよいのですが、それが「だいたいどのような色なのか」を計算してくれる機能はOS内にも各種オープンソース系のプログラムにもなさそうです。

「だいたいGreen」といった判定を行なってほしいケースはいろいろあります。

textedit2.png

のように、他の書類から取得してきた色のリストに対して「Color 1」といった表示を行うよりは「Black」のように表示できたほうがいいに決まっています。

そこで、ざっくりと計算してみることにしてみました。あまり真面目ではない方法で。

赤、青、黄色といった主要な色(Color Domain)を決定し、それらとの色差を計算して一番(距離が)近いものを求め、「赤に近い色だから赤」といった推測を行います。

col22.png
▲上記プログラムに実際に色推測ロジックを組み込んで動かしてみたところ

ただし、推測のロジックが穴だらけなので、白に近い色(薄い水色)などを「白」と判定するケースも多々ありますし、そもそも緑(Green)と青(Blue)の境界色など、判断基準が文化的バックボーンや年齢、職業によりかなりバラツキが出る箇所などがあるので、真剣にやりだすとキリがないし、案外ざっくりこの程度でもいいのかもしれません。

ただ、「灰色」(Gray)「白」(White)「黒」(Black)の判定はもっと主観的に行うべきだと思いますし、カラードメインのリストから上記の色を削除してみると、「もっとひどい計算結果」しか出てきません。水色(Light Blue)を灰色と推測するケースがひどかったので、灰色だけはリストから除去しました。

ColorDomainをもっと細かく設定するべきなのか、何か数値のRange的なもので定義するべきなのか(3次元的に?)、あるいは数値的なデータと知覚される色の違いがあって機械学習的な「データを大量に集めて特徴量を計算するけどロジックはよくわからない」的なやり方で結果を出すのか、いまひとつぱっと思いつきだけでは計算し切れません。そもそも、OS側にこういう機能は用意されていてほしいところです。

色差の計算結果を見ると、

–> {{colObj:(NSCachedRGBColor) NSCalibratedRGBColorSpace 1 1 1 1, colDist:25.499740796083, colName:”White”}, {colObj:(NSCachedRGBColor) NSCalibratedRGBColorSpace 0 0 0 1, colDist:90.533813281336, colName:”Black”}, {colObj:(NSCalibratedRGBColor) NSCalibratedRGBColorSpace 0.98 0 0.02 1, colDist:3.40282346638529E+38, colName:”Red”}, {colObj:(NSCalibratedRGBColor) NSCalibratedRGBColorSpace 0.99 0.99 0.04 1, colDist:3.40282346638529E+38, colName:”Yellow”}, {colObj:(NSCalibratedRGBColor) NSCalibratedRGBColorSpace 0.98 0 0.88 1, colDist:3.40282346638529E+38, colName:”Pink”}, {colObj:(NSCalibratedRGBColor) NSCalibratedRGBColorSpace 0.02 0 0.99 1, colDist:3.40282346638529E+38, colName:”Blue”}, {colObj:(NSCalibratedRGBColor) NSCalibratedRGBColorSpace 0.13 1 0.03 1, colDist:3.40282346638529E+38, colName:”Green”}, {colObj:(NSCalibratedRGBColor) NSCalibratedRGBColorSpace 0.42 0 0.99 1, colDist:3.40282346638529E+38, colName:”Purple”}, {colObj:(NSCalibratedRGBColor) NSCalibratedRGBColorSpace 0.11 0.9 0.99 1, colDist:3.40282346638529E+38, colName:”Light Blue”}}

などとおかしな値(3.40282346638529E+38)になっている箇所があり、その結果として計算結果に疑問が残る箇所がままありました。なんでしょうね、これは。

→ 普通にNSColorで作った色データとColoursの機能を使って作成した色データの色差を計算するとこの値になることが判明。ルーチン内部で色データはColoursの機能を使うように統一したら問題は発生しなくなりました。

AppleScript名:Coloursで指定色がだいたいどんな色なのか推測
– Created 2018-01-03 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “colorsKit” –https://github.com/bennyguitar/Colours

(*
http://piyocast.com/as/archives/5086
*)

property NSColor : a reference to current application’s NSColor
property NSArray : a reference to current application’s NSArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor

set {rVal, gVal, bVal} to choose color
set aRes to retColorDomainName(rVal, gVal, bVal, 65535) of me

on retColorDomainName(rVal, gVal, bVal, aColMax)
  set selNSCol to makeNSColorFromRGBAval(rVal, gVal, bVal, aColMax, aColMax) of me
  
  
set colorDomainList to {{colName:“Red”, colValue:{64615, 0, 1767}}, {colName:“Yellow”, colValue:{65530, 65344, 2686}}, {colName:“Pink”, colValue:{64551, 0, 57891}}, {colName:“Blue”, colValue:{1636, 0, 65417}}, {colName:“Green”, colValue:{8866, 65535, 2217}}, {colName:“Black”, colValue:{0, 0, 0}}, {colName:“White”, colValue:{65535, 65535, 65535}}, {colName:“Purple”, colValue:{27963, 0, 65430}}, {colName:“Light Blue”, colValue:{7557, 59465, 65463}}}
  
  
set nsColList to {}
  
repeat with i in colorDomainList
    set aColV to colValue of i
    
set aColN to colName of i
    
copy aColV to {rVal, gVal, bVal}
    
set aColor to makeNSColorFromRGBAval(rVal, gVal, bVal, 65535, 65535)
    
set aDist to (selNSCol’s distanceFromColor:aColor type:2) –ColorDistanceCIE2000
    
set the end of nsColList to {colObj:aColor, colDist:aDist, colName:aColN}
  end repeat
  
  
set aRes to sort1DRecList(nsColList, “colDist”, true) of me
  
set aFirst to first item of aRes
  
set aName to colName of aFirst
  
  
return aName
end retColorDomainName

on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
  set aRedCocoa to (redValue / aMaxVal) as real
  
set aGreenCocoa to (greenValue / aMaxVal) as real
  
set aBlueCocoa to (blueValue / aMaxVal) as real
  
set aAlphaCocoa to (alphaValue / aMaxVal) as real
  
set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
  
return aColor
end makeNSColorFromRGBAval

–数値の1D List with Recordをソート
on sort1DRecList(aList as list, aKey as string, ascendingF as boolean)
  set aArray to NSArray’s arrayWithArray:aList
  
set desc1 to NSSortDescriptor’s sortDescriptorWithKey:aKey ascending:ascendingF selector:“compare:”
  
set bList to (aArray’s sortedArrayUsingDescriptors:{desc1}) as list
  
return bList
end sort1DRecList

★Click Here to Open This Script 

2018/01/05 Coloursで指定色の輝度をMAX状態に変更した色を取得する

オープンソースの「Colours」(By Ben Gordon)をフレームワーク化したcolorsKitを呼び出して、指定色の輝度を最大状態に変更した色のデータを計算するAppleScriptです。

4471e317-f308-4575-81fa-9cfe7b3bca1a.png

実行には、colorsKit.frameworkのインストールを必要とします。アーカイブをダウンロードして展開し、~/Library/Frameworksフォルダに入れてください(あくまで自己責任で)。

もともと、Coloursにlighten: というメソッドがあり、「指定色の現在の輝度よりも相対的にどの程度明るくするか」という動作内容だったので、最大値(1.0)を指定すると、現在の値+1.0という結果になってしまい、最大値(1.0)を超える数値が返ってきていました。そのため、AppleScript側でNSColorから値を取得する際に最大値チェックを行なっています。

–> Download Framework Binary

AppleScript名:Coloursで指定色の輝度をMAX状態に変更した色を取得する
– Created 2018-01-03 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “colorsKit” –https://github.com/bennyguitar/Colours
–http://piyocast.com/as/archives/5085

property NSColor : a reference to current application’s NSColor

set {rVal, gVal, bVal} to choose color
set aCol to makeNSColorFromRGBAval(rVal, gVal, bVal, 65535, 65535) of me

set bCol to aCol’s lighten:1.0

set cList to retColListFromNSColor(bCol, 65535)
choose color default color cList

on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
  set aRedCocoa to (redValue / aMaxVal) as real
  
set aGreenCocoa to (greenValue / aMaxVal) as real
  
set aBlueCocoa to (blueValue / aMaxVal) as real
  
set aAlphaCocoa to (alphaValue / aMaxVal) as real
  
set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
  
return aColor
end makeNSColorFromRGBAval

–NSColorからRGBの値を取り出す
on retColListFromNSColor(aCol, aMAX as integer)
  set aRed to round ((aCol’s redComponent()) * aMAX) rounding as taught in school
  
set aGreen to round ((aCol’s greenComponent()) * aMAX) rounding as taught in school
  
set aBlue to round ((aCol’s blueComponent()) * aMAX) rounding as taught in school
  
  
if aRed > aMAX then set aRed to aMAX
  
if aGreen > aMAX then set aGreen to aMAX
  
if aBlue > aMAX then set aBlue to aMAX
  
  
return {aRed, aGreen, aBlue}
end retColListFromNSColor

★Click Here to Open This Script 

2018/01/04 Coloursで色バリエーション展開を計算して表示 v2

オープンソースの「Colours」(By Ben Gordon)をフレームワーク化したcolorsKitを呼び出して、指定色のカラーバリエーションを4通り計算して表示するAppleScriptです。

実行には、colorsKit.frameworkのインストールを必要とします。アーカイブをダウンロードして展開し、~/Library/Frameworksフォルダに入れてください(あくまで自己責任で)。スクリプトエディタ上でControl-Command-Rにより実行します。

–> Download Framework Binary

col2.png

col1_resized.png

Coloursが用意しているColorSchemeAnalagous, ColorSchemeMonochromatic, ColorSchemeTriad, ColorSchemeComplementaryの4通りのカラーバリエーション計算を行なってNSColorWellで表示します。

AppleScript名:Colorsで色バリエーション展開を計算して表示 v2
– Created 2017-12-20 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “colorsKit” –https://github.com/bennyguitar/Colours
–http://piyocast.com/as/archives/5081

property NSView : a reference to current application’s NSView
property NSColor : a reference to current application’s NSColor
property NSString : a reference to current application’s NSString
property NSScreen : a reference to current application’s NSScreen
property NSButton : a reference to current application’s NSButton
property NSWindow : a reference to current application’s NSWindow
property NSColorWell : a reference to current application’s NSColorWell
property NSWindowController : a reference to current application’s NSWindowController

property windisp : false

set aWidth to 500
set aHeight to 250

set {rVal, gVal, bVal} to choose color
set aNSCol to makeNSColorFromRGBAval(rVal, gVal, bVal, 65536, 65536) of me

dispCustomView(aWidth, aHeight, “Color Variation Result”, “OK”, 180, aNSCol) of me

on dispCustomView(aWidth as integer, aHeight as integer, aTitle as text, aButtonMSG as text, timeOutSecs as number, aNSCol)
  
  
–Check If this script runs in foreground
  
if not (current application’s NSThread’s isMainThread()) as boolean then
    error “This script must be run from the main thread (Command-Control-R in Script Editor).”
  end if
  
  
set (my windisp) to true
  
  
  
–Buttonをつくる
  
set bButton to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(aWidth / 4, 0, aWidth / 2, 40)))
  
bButton’s setTitle:aButtonMSG
  
bButton’s setButtonType:(current application’s NSMomentaryLightButton)
  
bButton’s setBezelStyle:(current application’s NSRoundedBezelStyle)
  
bButton’s setKeyEquivalent:(return)
  
bButton’s setTarget:me
  
bButton’s setAction:(“clicked:”)
  
  
–NSViewをつくる
  
set aNSV to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aHeight, aWidth))
  
aNSV’s addSubview:bButton
  
aNSV’s setNeedsDisplay:true
  
  
–NSColorWellをつくる
  
repeat with ii from 0 to 3 by 1
    set colorArray1 to (aNSCol’s colorSchemeOfType:ii) as list
    
set the beginning of colorArray1 to aNSCol
    
set tmpLen to (length of colorArray1)
    
set aStep to 0
    
repeat with i in colorArray1
      set aColorWell to (NSColorWell’s alloc()’s initWithFrame:(current application’s NSMakeRect((100 * aStep), (((3 - ii) * 50) + 40), 100, 40)))
      (
aColorWell’s setColor:i)
      (
aColorWell’s setBordered:true)
      (
aNSV’s addSubview:aColorWell)
      
set aStep to aStep + 1
    end repeat
  end repeat
  
  
set aWin to makeWinWithView(aNSV, aWidth, aHeight, aTitle, 1.0)
  
  
set wController to NSWindowController’s alloc()
  
wController’s initWithWindow:aWin
  
wController’s showWindow:me
  
  
aWin’s makeKeyAndOrderFront:me
  
  
set aCount to timeOutSecs * 10 –timeout seconds * 10
  
repeat aCount times
    if (my windisp) = false then
      exit repeat
    end if
    
delay 0.1
  end repeat
  
  
my closeWin:aWin
  
end dispCustomView

–Button Clicked Event Handler
on clicked:aSender
  set (my windisp) to false
end clicked:

–make Window for Input
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
  set aScreen to NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
  
set aBacking to current application’s NSTitledWindowMask
  
set aDefer to current application’s NSBackingStoreBuffered
  
  
– Window
  
set aWin to NSWindow’s alloc()
  (
aWin’s initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
  
  
aWin’s setTitle:aTitle
  
aWin’s setDelegate:me
  
aWin’s setDisplaysWhenScreenProfileChanges:true
  
aWin’s setHasShadow:true
  
aWin’s setIgnoresMouseEvents:false
  
aWin’s setLevel:(current application’s NSNormalWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setAlphaValue:alphaV –append
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
aWin’s makeKeyAndOrderFront:(me)
  
  
– Set Custom View
  
aWin’s setContentView:aView
  
  
return aWin
end makeWinWithView

–close win
on closeWin:aWindow
  repeat with n from 10 to 1 by -1
    (aWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
aWindow’s |close|()
end closeWin:

on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
  set aRedCocoa to (redValue / aMaxVal) as real
  
set aGreenCocoa to (greenValue / aMaxVal) as real
  
set aBlueCocoa to (blueValue / aMaxVal) as real
  
set aAlphaCocoa to (alphaValue / aMaxVal) as real
  
set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
  
return aColor
end makeNSColorFromRGBAval

★Click Here to Open This Script 

2018/01/04 Numbers上で選択中の列のデータからIPアドレスを抽出

Numbersで選択中の列のセルからデータを取得して、正規表現でIPアドレスとおぼしき文字列をリスト(配列)で抽出するAppleScriptです。

アクセス解析データをWebブラウザからNumbersにコピペした状態で、IPアドレスとして出力される(ドメイン名などが不明)ものを別サービスで位置情報やドメイン情報を計算するために作ったものです。

Numbersの表データ上で処理対象範囲のセルを選択しておいて実行するとスクリプトエディタの結果欄に出力されます。

scrn1.png

開発環境のマシン(MacBook Pro 2012 Retina)で1,000件程度のデータからの抽出が0.08秒程度です。

AppleScript名:Numbers上で選択中の列のデータからIPアドレスを抽出
– Created 2018-01-03 by Takaaki Naganoya
– 2018 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/5080

property NSPredicate : a reference to current application’s NSPredicate
property NSArray : a reference to current application’s NSArray

set aList to getSelectionDataFromNumbers() of me
set anArray to NSArray’s arrayWithArray:aList
set aPred to NSPredicate’s predicateWithFormat:“SELF MATCHES ’[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}’”
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
–>  {”XX.XX.XX.XXX”, “XXX.XX.XXX.XXX”, “XXX.XXX.XX.XXX”, …..}

on getSelectionDataFromNumbers()
  tell application “Numbers”
    tell front document
      tell active sheet
        tell table 1
          set aList to value of every cell of selection range
        end tell
      end tell
    end tell
  end tell
  
return aList
end getSelectionDataFromNumbers

★Click Here to Open This Script 

2017/12/30 日本語の慣用句を検索する

オープンソースの「DictionaryKit」(By Mattt)を用いて、「スーパー大辞林」から日本語の慣用句を検索するAppleScriptです。

興味のある方は、DictionaryKitをフレームワーク化したdictKit.frameworkのバイナリをOS X 10.10以降用にビルドしたものをダウンロードして~/Library/Frameworksフォルダに入れておためしください。

–> Download Framework Binary

「血」 → {”血が通う”, “血が騒ぐ”, “血が繫がる”, “血が上る”, “血が引く”, “血で血を洗う”, “血と汗”, “血となり肉となる”, “血に飢える”, “血の出るよう”, “血の滲むよう”, “血は争えない”, “血は水よりも濃い”, “血も涙もない”, “血湧き肉躍る”, “血を受ける”, “血を歃る”, “血を吐く思い”, “血を引く”, “血を見る”, “血を分ける”}

AppleScript名:日本語の慣用句を検索する
– Created 2017-12-30 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “dictKit” –https://github.com/mattt/DictionaryKit
–http://piyocast.com/as/archives/5077

set aRes to retJapaneseIdionFromKanjiChar(“血”) of me
–>  {”血が通う”, “血が騒ぐ”, “血ががる”, “血が上る”, “血が引く”, “血で血を洗う”, “血と汗”, “血となり肉となる”, “血に飢える”, “血の出るよう”, “血の滲むよう”, “血は争えない”, “血は水よりも濃い”, “血も涙もない”, “血湧き肉躍る”, “血を受ける”, “血を歃る”, “血を吐く思い”, “血を引く”, “血を見る”, “血を分ける”}

set aRes to retJapaneseIdionFromKanjiChar(“家”) of me
–>  {”家給し人足る”, “家高し”, “家に杖つく”, “家貧しくして孝子顕わる”, “家をあける”, “家を出ず”, “家を外にする”}

set aRes to retJapaneseIdionFromKanjiChar(“水”) of me
–>  {”水到りて渠成る”, “水が合わない”, “水が漬く”, “水が入る”, “水が引く”, “水涸る”, “水清ければ魚棲まず”, “水澄む”, “水で割る”, “水と油”, “水にする”, “水に流す”, “水になる”, “水に馴れる”, “水温む”, “水の滴るよう”, “水の流れと身のゆくえ”, “水の低きに就く如し”, “水は方円の器に随う”, “水も漏らさぬ”, “水をあける”, “水を打ったよう”, “水を得た魚のよう”, “水を掛ける”, “水をさす”, “水を向ける”}

set aRes to retJapaneseIdionFromKanjiChar(“木”) of me
–>  {”木から落ちた猿”, “樹静かならんと欲すれども風止まず”, “木で鼻を括る”, “木に竹を接ぐ”, “木にも草にも心を置く”, “木に餅がなる”, “木に縁りて魚を求む”, “木の股から生まれる”, “木六竹八塀十郎”, “木を見て森を見ず”}

on retJapaneseIdionFromKanjiChar(aKanji)
  set aDictionary to (current application’s TTTDictionary’s dictionaryNamed:“スーパー大辞林”)
  
set hitEntryList to (aDictionary’s entriesForSearchTerm:aKanji) as list
  
if hitEntryList is not equal to {missing value} then
    
    
repeat with ii in hitEntryList
      set j to contents of ii
      
      
set headW to (j’s headword)
      
set headW to headW as text
      
      
try
        set aText to (j’s |text|)
        
set aText to aText as text
      on error
        set aText to (j’s HTML)
        
set aText to decodeCharacterReference(aText) of me
      end try
      
      
if aText contains “〈句項目〉” then
        set aCount to 1
        
set tmpList to paragraphs of aText
        
set aLen to length of tmpList
        
        
repeat with i in tmpList
          set j to contents of i
          
if j contains “〈句項目〉” then
            set outList to items (aCount + 1) thru -1 of tmpList
            
exit repeat
          end if
          
set aCount to aCount + 1
        end repeat
        
        
repeat with ii from (aCount + 1) to aLen
          set jj to contents of ii
          
if jj = “” then exit repeat
        end repeat
        
        
set outList to contents of items (aCount + 1) thru (ii - 1) of tmpList
        
return outList
      end if
    end repeat
    
    
return {}
  end if
end retJapaneseIdionFromKanjiChar

on decodeCharacterReference(aStr)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF16StringEncoding)
  
set styledString to current application’s NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)
  
set plainText to (styledString’s |string|()) as string
  
return plainText
end decodeCharacterReference

★Click Here to Open This Script 

2017/12/28 iBooksライブラリ中のepubファイルからメタ情報を取得 v3

iBooksライブラリ中のbook(ePub)ファイルからメタ情報(属性情報)を取得するAppleScriptです。

iBooks.app自体はまったくScriptableでもなんでもないですが、ライブラリ内の各ファイルから直接情報を取得できます。各book書類はread onlyなうえに本文の内容は暗号化されているので、この程度で問題ないでしょう。

実行にはShane StanleyのAppleScriptライブラリ「Metadata Lib」のインストールが必要です。

AppleScript名:iBooksライブラリ中のepubファイルから情報を取得 v3
– Created 2017/02/26 by Christopher Stone
– Modified 2017/11/01 by Takaaki Naganoya
use framework “Foundation”
use scripting additions
use mdLib : script “Metadata Lib” version “1.0.0″
–http://piyocast.com/as/archives/5074

property NSString : a reference to current application’s NSString
property NSMutableArray : a reference to current application’s NSMutableArray
property NSPropertyListFormat : a reference to current application’s NSPropertyListFormat
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSPropertyListImmutable : a reference to current application’s NSPropertyListImmutable
property NSPropertyListSerialization : a reference to current application’s NSPropertyListSerialization

set aPath to POSIX path of (path to library folder from user domain)
set sourceFolder to aPath & “Containers/com.apple.BKAgentService/Data/Documents/iBooks/Books/”

set textFiles to mdLib’s searchFolders:{sourceFolder} searchString:“kMDItemContentType contains %@ || kMDItemContentType contains %@ “ searchArgs:{“com.apple.ibooks-folder”, “org.idpf.epub-container”}

set outDicList to NSMutableArray’s new()
repeat with i in textFiles
  set aFile to (i as string)
  
  
if aFile ends with “/” then
    set aFullPath to aFile & “iTunesMetadata.plist”
  else
    set aFullPath to aFile & “/iTunesMetadata.plist”
  end if
  
  
try
    set xmlData to read ((POSIX file aFullPath) as alias) as «class utf8»
    
set xRes to readPlistFromStr(xmlData) of me
    
log xRes as list of string or string –as anything (maybe record or missing value)
    
    
(*cover-writing-mode:vertical, genre:教育, scroll-axis:default, sort-artist:あっぷる, BKITunesMigratedMetadata:PersistentID:6.68315366592803E+18, seriesTitle:Everyone Can Code, sort-name:Swiftによるあぷりけーしょん開発:入門編, itemId:1209648719, apple-id:xxxxxxxxxxxx@xxx.xxx, fileExtension:ibooks, year:2017, releaseDate:2017-03-19T07:00:00Z, BKInsertionDate:512115887, asset-info:flavor:pluspub, file-size:60642970, book-info:publication-version:162901775, PageProgression:default, asset-info:flavor:pluspub, file-size:60642970, package-file-hash:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX, BKAllocatedSize:82993152, longDescription:このコースでは、Swiftという言語を使って基本的なプログラミングの土台をしっかりと作り上げていきます。基本的なiOSアプリケーションを一から開発するために必要なツール、手法、概念を活用して、実践的な練習に取り組みます。さらに、プログラミングと優れたアプリケーション開発の土台となる、ユーザーインターフェイス設計の基本原則についても学習します。このコースを受講するにあたってプログラミングの経験は必要ありません。プログラミングの経験がある場合、レッスンの最初の方は簡単に読み進めていただくとよいでしょう。このブックではプログラミングの基礎にとどまらず、ソフトウェア開発ツールや概念、ベストプラクティスについても学習できます。, artistId:9.39801385E+8, artistName:Apple Education, isPreview:false, BKDisplayName:mzbf.eqmpijqw..d2.dlv.d2.dlv.ibooks, human-friendly-publication-version:1.1, shouldDisableTouchEmulation:true, vendorId:379015, drmVersionNumber:0, kind:ebook, s:143462, genreId:10037, explicit:2, seriesAdamId:1.118575554E+9, publisher:Apple Inc. - Education, versionRestrictions:16843008, BKGenerationCount:2, desktopSupportLevel:supported, primaryLanguage:ja, itemName:Swiftによるアプリケーション開発:入門編, purchaseDate:2017-03-25T06:21:26Z, shouldDisableOptimizeSpeed:true, obeyPageBreaks:1, pageCount:244*)
    
if xRes is not equal to missing value then
      (outDicList’s addObject:xRes)
    end if
  end try
end repeat

return outDicList as list of string or string –as anything

–stringのplistを読み込んでNSDictionaryに
on readPlistFromStr(theString)
  set aSource to NSString’s stringWithString:theString
  
set pListData to aSource’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aPlist to NSPropertyListSerialization’s propertyListFromData:pListData mutabilityOption:(NSPropertyListImmutable) |format|:(NSPropertyListFormat) errorDescription:(missing value)
  
return aPlist
end readPlistFromStr

★Click Here to Open This Script 

2017/12/26 TextEditで最前面のドキュメント内で使用されている色情報を抽出して処理対象色を選択

テキストエディット上で色をつけた箇所を抽出して、ポップアップメニューから対象色を選択し、指定の色の箇所のテキストを取り出すAppleScriptです。

textedit1_resized.png

Mac標準搭載のテキストエディタ「テキストエディット」はAppleScriptからコントロールできるため、行数や文字数を数えるという原始的な処理はもちろんのこと、赤く色をつけた箇所のみを抽出するとかフォントを変更した箇所だけを抽出するといった高度な「AppleScriptらしい」処理が可能です。

黒以外の色が指定された文字の箇所を抽出するAppleScriptにポップアップメニューを動的に生成する機能を付加して、抽出対象色を自由に指定できるようにしてみました。

テキストエディタ上でリッチテキストモードの書類をオープンして一部に色をつけた状態で、AppleScriptをスクリプトエディタ上でControl-Command-Rにより実行します。

Scriptの大部分は動的にWindowとボタンとポップアップメニューを生成、およびクリック時のイベントを処理するためのもので、テキストエディットと通信を行なって書式情報や文字列を取得する部分はごく一部です。

textedit2.png

textedit3.png

textedit4.png

AppleScript名:TextEditで最前面のドキュメント内で使用されている色情報を抽出して処理対象色を選択
– Created 2017-12-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “Carbon” – AEInteractWithUser() is in Carbon
–http://piyocast.com/as/archives/5071

property NSView : a reference to current application’s NSView
property NSColor : a reference to current application’s NSColor
property NSArray : a reference to current application’s NSArray
property NSMenu : a reference to current application’s NSMenu
property NSImage : a reference to current application’s NSImage
property NSScreen : a reference to current application’s NSScreen
property NSButton : a reference to current application’s NSButton
property NSWindow : a reference to current application’s NSWindow
property NSTextField : a reference to current application’s NSTextField
property NSMenuItem : a reference to current application’s NSMenuItem
property NSBezierPath : a reference to current application’s NSBezierPath
property NSPopUpButton : a reference to current application’s NSPopUpButton
property NSWindowController : a reference to current application’s NSWindowController
property NSTitledWindowMask : a reference to current application’s NSTitledWindowMask
property NSRoundedBezelStyle : a reference to current application’s NSRoundedBezelStyle
property NSNormalWindowLevel : a reference to current application’s NSNormalWindowLevel
property NSBackingStoreBuffered : a reference to current application’s NSBackingStoreBuffered
property NSMomentaryLightButton : a reference to current application’s NSMomentaryLightButton

property windisp : false

if current application’s AEInteractWithUser(-1, missing value, missing value) is not equal to 0 then return

tell application “TextEdit”
  set dCount to count every document
  
if dCount = 0 then return
  
tell text of front document
    set aList to color of every character
  end tell
end tell

–1D/2D Listのユニーク化
set ap1List to uniquify1DList(aList, true) of me

–色選択ダイアログを表示してポップアップメニューから色選択
set aButtonMSG to “OK”
set aWindowTitle to “Choose Color”

set aVal to getPopupValues(ap1List, 65535, aButtonMSG, aWindowTitle, 180) of me

if (aVal = false) or (aVal = missing value) then
  display dialog “No Selection” buttons {“OK”} default button 1 with icon 2
  
return
end if

set targColor to item aVal of ap1List
set aRes to pickupColoredText(targColor) of me
set the clipboard to aRes –抽出結果をクリップボードへ
return aRes

on getPopupValues(ap1List, aColMax, aButtonMSG, aWindowMSG, timeOutSecs)
  
  
set (my windisp) to true
  
  
set aView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 360, 100))
  
  
–Labelをつくる
  
set a1TF to NSTextField’s alloc()’s initWithFrame:(current application’s NSMakeRect(30, 60, 80, 20))
  
a1TF’s setEditable:false
  
a1TF’s setStringValue:“Color:”
  
a1TF’s setDrawsBackground:false
  
a1TF’s setBordered:false
  
  
–Ppopup Buttonをつくる
  
set a1Button to NSPopUpButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(80, 60, 200, 20)) pullsDown:false
  
a1Button’s removeAllItems()
  
  
set a1Menu to NSMenu’s alloc()’s init()
  
  
set iCount to 1
  
repeat with i in ap1List
    copy i to {r1, g1, b1}
    
    
set nsCol to makeNSColorFromRGBAval(r1, g1, b1, aColMax, aColMax) of me
    
set anImage to makeNSImageWithFilledWithColor(64, 64, nsCol) of me
    
    
set aTitle to “Color_” & (iCount as string)
    
set aMenuItem to (NSMenuItem’s alloc()’s initWithTitle:aTitle action:“actionHandler:” keyEquivalent:“”)
    (
aMenuItem’s setImage:anImage)
    (
aMenuItem’s setEnabled:true)
    (
a1Menu’s addItem:aMenuItem)
    
    
set iCount to iCount + 1
  end repeat
  
  
a1Button’s setMenu:a1Menu
  
  
  
–Buttonをつくる
  
set bButton to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(80, 10, 140, 40)))
  
bButton’s setButtonType:(current application’s NSMomentaryLightButton)
  
bButton’s setBezelStyle:(current application’s NSRoundedBezelStyle)
  
bButton’s setTitle:aButtonMSG
  
bButton’s setTarget:me
  
bButton’s setAction:(“clicked:”)
  
bButton’s setKeyEquivalent:(return)
  
  
aView’s addSubview:a1TF
  
  
aView’s addSubview:a1Button
  
aView’s addSubview:bButton
  
aView’s setNeedsDisplay:true
  
  
–NSWindowControllerを作ってみた
  
set aWin to (my makeWinWithView(aView, 300, 100, aWindowMSG))
  
  
set wController to NSWindowController’s alloc()
  
wController’s initWithWindow:aWin
  
  
wController’s showWindow:me
  
  
set aCount to timeOutSecs * 100
  
  
set hitF to false
  
repeat aCount times
    if (my windisp) = false then
      set hitF to true
      
exit repeat
    end if
    
delay 0.01
    
set aCount to aCount - 1
  end repeat
  
  
my closeWin:aWin
  
  
if hitF = true then
    set s1Val to (a1Button’s indexOfSelectedItem() as integer) + 1
  else
    set s1Val to false
  end if
  
  
return s1Val
  
end getPopupValues

on clicked:aSender
  set (my windisp) to false
end clicked:

–make Window for Display
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle)
  set aScreen to NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
  
  
set aBacking to current application’s NSTitledWindowMask
  
  
set aDefer to current application’s NSBackingStoreBuffered
  
  
– Window
  
set aWin to NSWindow’s alloc()
  (
aWin’s initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
  
–aWin’s setBackgroundColor:(current application’s NSColor’s whiteColor())
  
  
aWin’s setTitle:aTitle
  
aWin’s setDelegate:me
  
aWin’s setDisplaysWhenScreenProfileChanges:true
  
aWin’s setHasShadow:true
  
aWin’s setIgnoresMouseEvents:false
  
aWin’s setLevel:(current application’s NSNormalWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
–aWin’s makeKeyAndOrderFront:(me)
  
  
aWin’s setContentView:aView
  
  
return aWin
  
end makeWinWithView

–close win
on closeWin:aWindow
  repeat with n from 10 to 1 by -1
    (aWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
aWindow’s |close|()
end closeWin:

–Popup Action Handler
on actionHandler:sender
  set aTag to tag of sender as integer
  
set aTitle to title of sender as string
end actionHandler:

on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
  set aRedCocoa to (redValue / aMaxVal) as real
  
set aGreenCocoa to (greenValue / aMaxVal) as real
  
set aBlueCocoa to (blueValue / aMaxVal) as real
  
set aAlphaCocoa to (alphaValue / aMaxVal) as real
  
set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
  
return aColor
end makeNSColorFromRGBAval

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

–指定サイズのNSImageを作成し、指定色で塗って返す
on makeNSImageWithFilledWithColor(aWidth, aHeight, fillColor)
  set anImage to NSImage’s alloc()’s initWithSize:(current application’s NSMakeSize(aWidth, aHeight))
  
anImage’s lockFocus()
  

  
set theRect to {{x:0, y:0}, {height:aHeight, width:aWidth}}
  
set theNSBezierPath to NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  

  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  

  
anImage’s unlockFocus()
  

  
return anImage
end makeNSImageWithFilledWithColor

on pickupColoredText(aColList)
  set outStrList to “”
  
  
tell application “TextEdit”
    tell text of front document
      set colorList to color of every attribute run
      
set textList to characters of every attribute run
      
      
set aCount to length of colorList
      
      
repeat with i from 1 to aCount
        set aColCon to item i of colorList
        
if aColCon is equal to aColList then –指定色の箇所
          set outStrList to outStrList & ((contents of item i of textList) as string) & return
        end if
      end repeat
      
    end tell
  end tell
  
  
return outStrList
end pickupColoredText

★Click Here to Open This Script 

2017/12/26 Numbersから緯度経度情報を取得して地図にプロット v3

Numbersでオープン中の書類の現在選択中のシートにある表の内容を読み取って、MKMapViewの地図にピンで位置をプロットして表示するAppleScriptです。

実行にはShane StanleyのAppleScriptライブラリ「BridgePlus」のインストールを必要とします。

実行時にはNumbersで「ピン名称」「緯度」「経度」のデータをオープンしておく必要があります。

→ sample data (Numbers)

実行にはスクリプトエディタ上でControlーCommandーRを実行してください。

前バージョンからは、

  Pinデータ作成時にプログレスバーを表示
  地図種別切り替え

を追加してみました。680箇所の位置データをプロットさせてみたら10秒程度かかったので、プログレスバーを表示させたらいい感じでした。

map40_resized.png

map41_resized.png

map42_resized.png

map43_resized.png

AppleScript名:Numbersから緯度経度情報を取得して地図にプロット v3(プログレスバー+セグメント)
– Created 2017-12-20 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “MapKit”
use framework “CoreLocation”
use bPlus : script “BridgePlus”
–http://piyocast.com/as/archives/5065

property NSView : a reference to current application’s NSView
property NSScreen : a reference to current application’s NSScreen
property NSButton : a reference to current application’s NSButton
property SMSForder : a reference to current application’s SMSForder
property NSWindow : a reference to current application’s NSWindow
property MKMapView : a reference to current application’s MKMapView
property MKMapTypeHybrid : a reference to current application’s MKMapTypeHybrid
property MKPointAnnotation : a reference to current application’s MKPointAnnotation
property MKMapTypeSatellite : a reference to current application’s MKMapTypeSatellite
property NSWindowController : a reference to current application’s NSWindowController
property NSTitledWindowMask : a reference to current application’s NSTitledWindowMask
property MKMapTypeStandard : a reference to current application’s MKMapTypeStandard
property NSSegmentedControl : a reference to current application’s NSSegmentedControl
property NSNormalWindowLevel : a reference to current application’s NSNormalWindowLevel
property NSBackingStoreBuffered : a reference to current application’s NSBackingStoreBuffered
property NSSegmentStyleTexturedRounded : a reference to current application’s NSSegmentStyleTexturedRounded

property windisp : false
property selSeg : 0
property aMapView : missing value

–データを取得する
set locList to getDataFromNumbersDoc() of me

set aWidth to 800
set aHeight to 600

set segTitleList to {“Map”, “Satellite”, “Satellite + Map”}
set tableTitle to retCurNumbersDocsTableName() of me
dispMapView(aWidth, aHeight, tableTitle, “OK”, 180, locList, segTitleList) of me

on dispMapView(aWidth as integer, aHeight as integer, aTitle as text, aButtonMSG as text, timeOutSecs as number, locList, segTitleList)
  –Check If this script runs in foreground
  
if not (current application’s NSThread’s isMainThread()) as boolean then
    error “This script must be run from the main thread (Command-Control-R in Script Editor).”
  end if
  
  
set selSeg to 0
  
set (my windisp) to true
  
  
  
  
–NSViewをつくる
  
set aNSView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aHeight, aWidth))
  
  
aNSView’s setNeedsDisplay:true
  
  
set aWin to makeWinWithView(aNSView, aWidth, aHeight, aTitle, 1.0)
  
  
set wController to NSWindowController’s alloc()
  
wController’s initWithWindow:aWin
  
wController’s showWindow:me
  
aWin’s makeKeyAndOrderFront:me –Windowを表示状態に
  
  
  
–Progress Barをつくる
  
set aPBar to current application’s NSProgressIndicator’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 40, aWidth, 40))
  
aPBar’s setMaxValue:(length of locList)
  
aPBar’s setMinValue:1
  
aPBar’s setIndeterminate:false
  
aPBar’s setControlSize:(current application’s NSProgressIndicatorPreferredLargeThickness)
  
aPBar’s setDoubleValue:(1.0 as real)
  
aNSView’s addSubview:aPBar
  
  
  
–MKMapViewをつくる
  
set aMapView to MKMapView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 40, aWidth, aHeight - 40))
  
aMapView’s setMapType:(current application’s MKMapTypeStandard)
  
  
aMapView’s setZoomEnabled:true
  
aMapView’s setScrollEnabled:true
  
aMapView’s setPitchEnabled:true
  
aMapView’s setRotateEnabled:false
  
aMapView’s setShowsCompass:true
  
aMapView’s setShowsZoomControls:true
  
aMapView’s setShowsScale:true
  
aMapView’s setShowsUserLocation:true
  
aMapView’s setDelegate:me
  
  
  
–MapにPinを追加
  
set aCount to 1
  
repeat with i in locList
    (aPBar’s setDoubleValue:(aCount as real)) –Update Progress Bar
    
    
copy i to {tmpAdr, tmpLat, tmpLong}
    
    
set aLocation to current application’s CLLocationCoordinate2DMake(tmpLat, tmpLong)
    
set anAnnotation to MKPointAnnotation’s alloc()’s init()
    (
anAnnotation’s setCoordinate:aLocation)
    (
anAnnotation’s setTitle:tmpAdr)
    (
aMapView’s addAnnotation:anAnnotation)
    
    
set aCount to aCount + 1
  end repeat
  
aPBar’s removeFromSuperview() –Remove Progress Bar
  
  
–Segmented Controlをつくる
  
set aSeg to makeSegmentedControl(segTitleList, aWidth, aHeight) of me
  
aNSView’s addSubview:aSeg
  
  
–MapViewをWindow上に表示  
  
copy middle item of locList to {tmpAdr, tmpLat, tmpLong}
  
set aLocation to current application’s CLLocationCoordinate2DMake(tmpLat, tmpLong)
  
aMapView’s setCenterCoordinate:aLocation zoomLevel:7 animated:false
  
aNSView’s addSubview:aMapView
  
  
–Buttonをつくる
  
set bButton to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(aWidth - 100, 0, 100, 40)))
  
bButton’s setTitle:aButtonMSG
  
bButton’s setButtonType:(current application’s NSMomentaryLightButton)
  
bButton’s setBezelStyle:(current application’s NSRoundedBezelStyle)
  
bButton’s setKeyEquivalent:(return)
  
bButton’s setTarget:me
  
bButton’s setAction:(“clicked:”)
  
aNSView’s addSubview:bButton
  
  
aWin’s makeFirstResponder:aMapView
  
  
set aCount to timeOutSecs * 10 –timeout seconds * 10
  
repeat aCount times
    if (my windisp) = false then
      exit repeat
    end if
    
delay 0.1
  end repeat
  
  
my closeWin:aWin
  
end dispMapView

–Button Clicked Event Handler
on clicked:aSender
  set (my windisp) to false
end clicked:

–make Window for Input
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
  set aScreen to NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
  
set aBacking to NSTitledWindowMask
  
set aDefer to NSBackingStoreBuffered
  
  
– Window
  
set aWin to NSWindow’s alloc()
  (
aWin’s initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
  
  
aWin’s setTitle:aTitle
  
aWin’s setDelegate:me
  
aWin’s setDisplaysWhenScreenProfileChanges:true
  
aWin’s setHasShadow:true
  
aWin’s setIgnoresMouseEvents:false
  
aWin’s setLevel:(NSNormalWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setAlphaValue:alphaV –append
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
aWin’s makeKeyAndOrderFront:(me)
  
  
– Set Custom View
  
aWin’s setContentView:aView
  
  
return aWin
end makeWinWithView

–close win
on closeWin:aWindow
  repeat with n from 10 to 1 by -1
    (aWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
aWindow’s |close|()
end closeWin:

on makeSegmentedControl(titleList, aWidth, aHeight)
  set aLen to length of titleList
  
  
set aSeg to NSSegmentedControl’s alloc()’s init()
  
aSeg’s setSegmentCount:aLen
  
  
set aCount to 0
  
repeat with i in titleList
    set j to contents of i
    (
aSeg’s setLabel:j forSegment:aCount)
    
set aCount to aCount + 1
  end repeat
  
  
aSeg’s setTranslatesAutoresizingMaskIntoConstraints:false
  
aSeg’s setSegmentStyle:(NSSegmentStyleTexturedRounded)
  
aSeg’s setFrame:(current application’s NSMakeRect(10, 5, 260, 30))
  
aSeg’s setTrackingMode:0
  
aSeg’s setTarget:me
  
aSeg’s setAction:“clickedSeg:”
  
aSeg’s setSelectedSegment:0
  
  
return aSeg
end makeSegmentedControl

–Numbersでオープン中の書類の選択中のシートの表1からデータを取得して2D Listに
on getDataFromNumbersDoc()
  
  
load framework
  
  
tell application “Numbers”
    if (count every document) = 0 then return false
    
    
tell front document
      if (count every sheet) = 0 then return false
      
      
tell active sheet
        tell table 1
          set colCount to column count
          
set rowCount to row count
          
set headerCount to header row count
          
set footerCount to footer row count
          
          
set dList to value of every cell of cell range
        end tell
      end tell
      
    end tell
  end tell
  
  
–Convert 1D List to 2D List
  
set bList to (SMSForder’s subarraysFrom:dList groupedBy:colCount |error|:(missing value)) as list
  
set sItem to 1 + headerCount
  
set eItem to rowCount - footerCount
  
set cList to items sItem thru eItem of bList
  
  
return cList
  
end getDataFromNumbersDoc

on retCurNumbersDocsTableName()
  tell application “Numbers”
    tell front document
      tell active sheet
        tell table 1
          return name
        end tell
      end tell
    end tell
  end tell
end retCurNumbersDocsTableName

on clickedSeg:aSender
  set aSel to aSender’s selectedSegment()
  
set selSeg to (aSel + 1)
  
set mapList to {MKMapTypeStandard, MKMapTypeSatellite, MKMapTypeHybrid}
  
set curMap to contents of item selSeg of mapList
  
aMapView’s setMapType:(curMap)
end clickedSeg:

★Click Here to Open This Script 

2017/12/23 Numbersから緯度経度情報を取得して地図にプロット

Numbersでオープン中の書類の現在選択中のシートにある表の内容を読み取って、MKMapViewの地図にピンで位置をプロットして表示するAppleScriptです。

map23_resized.png

スクリーンショット例は「戦場の絆」の導入されているゲームセンターの住所から緯度・経度情報を求めて本Scriptで地図表示させたものです。

Numbersの表示中のシート中の表からデータを読み取るわけですが、「ピンのタイトル」「緯度(Latitude)」「経度(Longitude)」でデータが構成されている必要があります。読み取り時にはとくにNumbers上でセルを選択しておく必要はありません。

→ sample data (Numbers)

map20_resized.png

実行にはShane StanleyのAppleScriptライブラリ「BridgePlus」のインストールを必要とします。

map21_resized.png

Numbersで「ピンのタイトル」「緯度(Latitude)」「経度(Longitude)」がセットになったデータをオープンした状態で、スクリプトエディタ上でControl-Command-Rと操作すると実行できます。

map30_resized.png

Script Menuに入れて実行することも可能ですが、その場合にはボタンをクリックさせてウィンドウをクローズすることができません。

map22_resized.png

AppleScript名:Numbersから緯度経度情報を取得して地図にプロット
– Created 2017-12-20 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “MapKit”
use framework “CoreLocation”
use bPlus : script “BridgePlus”
–http://piyocast.com/as/archives/5051

property |NSURL| : a reference to current application’s |NSURL|
property NSData : a reference to current application’s NSData
property NSView : a reference to current application’s NSView
property NSString : a reference to current application’s NSString
property NSScreen : a reference to current application’s NSScreen
property NSButton : a reference to current application’s NSButton
property NSWindow : a reference to current application’s NSWindow
property MKMapView : a reference to current application’s MKMapView
property NSURLRequest : a reference to current application’s NSURLRequest
property NSURLConnection : a reference to current application’s NSURLConnection
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSWindowController : a reference to current application’s NSWindowController
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

property windisp : false

–データを取得する
set locList to getDataFromNumbersDoc() of me

set aWidth to 800
set aHeight to 500

dispMapView(aWidth, aHeight, “Result”, “OK”, 180, locList) of me

on dispMapView(aWidth as integer, aHeight as integer, aTitle as text, aButtonMSG as text, timeOutSecs as number, locList)
  
  
–Check If this script runs in foreground
  
if not (current application’s NSThread’s isMainThread()) as boolean then
    error “This script must be run from the main thread (Command-Control-R in Script Editor).”
  end if
  
  
set (my windisp) to true
  
  
–MKMapViewをつくる
  
set aMapView to MKMapView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 40, aWidth, aHeight - 40))
  
aMapView’s setMapType:(current application’s MKMapTypeStandard)
  
  
aMapView’s setZoomEnabled:true
  
aMapView’s setScrollEnabled:true
  
aMapView’s setPitchEnabled:true
  
aMapView’s setRotateEnabled:false
  
aMapView’s setShowsCompass:true
  
aMapView’s setShowsZoomControls:true
  
aMapView’s setShowsScale:true
  
aMapView’s setShowsUserLocation:true
  
aMapView’s setDelegate:me
  
  
  
–MapにPinを追加
  
repeat with i in locList
    copy i to {tmpAdr, tmpLat, tmpLong}
    
    
set aLocation to current application’s CLLocationCoordinate2DMake(tmpLat, tmpLong)
    
set anAnnotation to current application’s MKPointAnnotation’s alloc()’s init()
    (
anAnnotation’s setCoordinate:aLocation)
    (
anAnnotation’s setTitle:tmpAdr)
    (
aMapView’s addAnnotation:anAnnotation)
  end repeat
  
  
copy middle item of locList to {tmpAdr, tmpLat, tmpLong}
  
set aLocation to current application’s CLLocationCoordinate2DMake(tmpLat, tmpLong)
  
aMapView’s setCenterCoordinate:aLocation zoomLevel:7 animated:false
  
  
–Buttonをつくる
  
set bButton to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(aWidth / 4, 0, aWidth / 2, 40)))
  
bButton’s setTitle:aButtonMSG
  
bButton’s setButtonType:(current application’s NSMomentaryLightButton)
  
bButton’s setBezelStyle:(current application’s NSRoundedBezelStyle)
  
bButton’s setKeyEquivalent:(return)
  
bButton’s setTarget:me
  
bButton’s setAction:(“clicked:”)
  
  
–SplitViewをつくる
  
set aSplitV to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aHeight, aWidth))
  
aSplitV’s addSubview:aMapView
  
aSplitV’s addSubview:bButton
  
aSplitV’s setNeedsDisplay:true
  
  
set aWin to makeWinWithView(aSplitV, aWidth, aHeight, aTitle, 1.0)
  
  
  
set wController to NSWindowController’s alloc()
  
wController’s initWithWindow:aWin
  
aWin’s makeFirstResponder:aMapView
  
wController’s showWindow:me
  
  
aWin’s makeKeyAndOrderFront:me
  
  
set aCount to timeOutSecs * 10 –timeout seconds * 10
  
repeat aCount times
    if (my windisp) = false then
      exit repeat
    end if
    
delay 0.1
  end repeat
  
  
my closeWin:aWin
  
end dispMapView

–Button Clicked Event Handler
on clicked:aSender
  set (my windisp) to false
end clicked:

–make Window for Input
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
  set aScreen to NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
  
set aBacking to current application’s NSTitledWindowMask
  
set aDefer to current application’s NSBackingStoreBuffered
  
  
– Window
  
set aWin to NSWindow’s alloc()
  (
aWin’s initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
  
  
aWin’s setTitle:aTitle
  
aWin’s setDelegate:me
  
aWin’s setDisplaysWhenScreenProfileChanges:true
  
aWin’s setHasShadow:true
  
aWin’s setIgnoresMouseEvents:false
  
aWin’s setLevel:(current application’s NSNormalWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setAlphaValue:alphaV –append
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
aWin’s makeKeyAndOrderFront:(me)
  
  
– Set Custom View
  
aWin’s setContentView:aView
  
  
return aWin
end makeWinWithView

–close win
on closeWin:aWindow
  repeat with n from 10 to 1 by -1
    (aWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
aWindow’s |close|()
end closeWin:

–Numbersでオープン中の書類の選択中のシートの表1からデータを取得して2D Listに
on getDataFromNumbersDoc()
  
  
load framework
  
  
tell application “Numbers”
    if (count every document) = 0 then return false
    
    
tell front document
      if (count every sheet) = 0 then return false
      
      
tell active sheet
        tell table 1
          set colCount to column count
          
set rowCount to row count
          
set headerCount to header row count
          
set footerCount to footer row count
          
          
set dList to value of every cell of cell range
        end tell
      end tell
      
    end tell
  end tell
  
  
–Convert 1D List to 2D List
  
set bList to (current application’s SMSForder’s subarraysFrom:dList groupedBy:colCount |error|:(missing value)) as list
  
set sItem to 1 + headerCount
  
set eItem to rowCount - footerCount
  
set cList to items sItem thru eItem of bList
  
  
return cList
  
end getDataFromNumbersDoc

★Click Here to Open This Script 

2017/12/20 指定IPアドレスの情報を取得してMKMapViewで表示

指定のIPアドレスの情報をIP-APIのREST APIを呼び出して取得し、緯度、経度情報からMKMapViewで地図を表示するAppleScriptです。

map1.png

map2.png

map3.png

map4.png

map5.png

前バージョンではshellのcurlコマンドでREST APIを呼び出していましたが、Shane Stanleyからツッコミが入って書き換えました。

フロントエンドプロセスで実行する必要があるため、スクリプトエディタ上でControl+Command-Rで実行します。ASObjC Explorer 4やScript Debugger上では表示できない可能性があります(あと、アプレットに書き出して実行するとエラーに、、、、)。

ずいぶん昔に作りかけて放置していたScriptに機能追加と修正を加えたものだったので、不思議な処理を行なっている部分がありました。初回掲載時からこれを修正しました。

AppleScript名:指定IPアドレスの情報を取得してMKMapViewで表示
– Created 2015-12-11 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “MapKit”
use framework “CoreLocation”
use framework “Carbon”
–http://piyocast.com/as/archives/5041

property |NSURL| : a reference to current application’s |NSURL|
property NSData : a reference to current application’s NSData
property NSString : a reference to current application’s NSString
property NSScreen : a reference to current application’s NSScreen
property NSButton : a reference to current application’s NSButton
property NSWindow : a reference to current application’s NSWindow
property NSSplitView : a reference to current application’s NSSplitView
property MKMapView : a reference to current application’s MKMapView
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSWindowController : a reference to current application’s NSWindowController
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

property windisp : false

set anIP to “175.139.227.170″

set aInfo to getIPAddressInfo(anIP) of me

if aInfo = missing value then
  display dialog “Network Error”
  
return
end if

set aLong to (lon of (aInfo as record)) as real
set aLat to (lat of (aInfo as record)) as real

set aWidth to 450
set aHeight to 300

set aTitle to “MKMapTypeStandard”
set aButtonMSG to “OK”

if current application’s AEInteractWithUser(-1, missing value, missing value) is not equal to 0 then return

dispMapView(aWidth, aHeight, aTitle, aButtonMSG, 180, aLat, aLong) of me

on dispMapView(aWidth as integer, aHeight as integer, aTitle as text, aButtonMSG as text, timeOutSecs as number, aLat, aLong)
  
  
set (my windisp) to true
  
  
–MKMapViewをつくる
  
set aMapView to MKMapView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
aMapView’s setMapType:(current application’s MKMapTypeStandard)
  
  
–MKMapTypeStandard –Works, First run may not display map texts
  
–MKMapTypeHybrid –Works
  
–MKMapTypeSatellite–Works
  
–MKMapTypeSatelliteFlyover–Works
  
–MKMapTypeHybridFlyover–Works
  
–MKMapTypeMutedStandard–Not Works (Error)
  
  
aMapView’s setZoomEnabled:true
  
aMapView’s setScrollEnabled:true
  
aMapView’s setPitchEnabled:true
  
aMapView’s setRotateEnabled:false
  
aMapView’s setShowsCompass:true
  
aMapView’s setShowsZoomControls:true
  
aMapView’s setShowsScale:true
  
aMapView’s setShowsUserLocation:false
  
  
set aLocation to current application’s CLLocationCoordinate2DMake(aLat, aLong)
  
aMapView’s setCenterCoordinate:aLocation zoomLevel:17 animated:false
  
aMapView’s setDelegate:me
  
  
  
–Buttonをつくる
  
set bButton to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, 40)))
  
bButton’s setTitle:aButtonMSG
  
bButton’s setKeyEquivalent:(return)
  
bButton’s setTarget:me
  
bButton’s setAction:(“clicked:”)
  
  
–SplitViewをつくる
  
set aSplitV to NSSplitView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aHeight, aWidth))
  
aSplitV’s setVertical:false
  
  
aSplitV’s addSubview:aMapView
  
aSplitV’s addSubview:bButton
  
aSplitV’s setNeedsDisplay:true
  
  
set aWin to makeWinWithView(aSplitV, aWidth, aHeight, aTitle, 1.0)
  
  
set wController to NSWindowController’s alloc()
  
wController’s initWithWindow:aWin
  
aWin’s makeFirstResponder:aMapView
  
wController’s showWindow:me
  
  
aWin’s makeKeyAndOrderFront:me
  
  
set aCount to timeOutSecs * 10 –timeout seconds * 10
  
repeat aCount times
    if (my windisp) = false then
      exit repeat
    end if
    
delay 0.1
  end repeat
  
  
my closeWin:aWin
  
end dispMapView

–Button Clicked Event Handler
on clicked:aSender
  set (my windisp) to false
end clicked:

–make Window for Input
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
  set aScreen to NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
  
set aBacking to current application’s NSTitledWindowMask –NSBorderlessWindowMask
  
set aDefer to current application’s NSBackingStoreBuffered
  
  
– Window
  
set aWin to NSWindow’s alloc()
  (
aWin’s initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
  
  
aWin’s setTitle:aTitle
  
aWin’s setDelegate:me
  
aWin’s setDisplaysWhenScreenProfileChanges:true
  
aWin’s setHasShadow:true
  
aWin’s setIgnoresMouseEvents:false
  
aWin’s setLevel:(current application’s NSModalPanelWindowLevel) – original : NSNormalWindowLevel
  
aWin’s setOpaque:false
  
aWin’s setAlphaValue:alphaV –append
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
aWin’s makeKeyAndOrderFront:(me)
  
  
– Set Custom View
  
aWin’s setContentView:aView
  
  
return aWin
end makeWinWithView

–close win
on closeWin:aWindow
  repeat with n from 10 to 1 by -1
    (aWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
aWindow’s |close|()
end closeWin:

–http://ip-api.com/docs/
on getIPAddressInfo(IPAddress)
  set link to “http://ip-api.com/json/” & IPAddress & “?fields=country,city,isp,org,as,mobile,proxy,message,lat,lon”
  
set theURL to |NSURL|’s URLWithString:link
  
set jsonData to NSData’s dataWithContentsOfURL:theURL
  
set aJsonDict to (NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value))
  
return aJsonDict
end getIPAddressInfo

★Click Here to Open This Script 

2017/12/19 指定IPアドレスの情報を取得してマップ.appで表示

指定のIPアドレスの情報をIP-APIのREST APIを呼び出して取得し、緯度、経度情報からマップ.appを検索するAppleScriptです。

maps_resized.png

マップ.appでの指定住所や指定緯度&経度の表示はURLイベント経由でしか行えないので、地図のローディング終了検出などは無理です(長めにdelayして時間待ちするしか)。

地図表示して地図をPDFに指定ファイル名で保存、というあたりまで処理できるとAppleScriptらしい感じがしますが、まだそこまでは行っていません。

GUI Scriptingを併用するとマップ.appからのPDF書き出しも行えそうですが、動的にMKMapViewを作成してビューの内容をPDF書き出ししたほうがよさそうです。

→ Xcode上でMKMapViewからの画像取得をAppleScriptでやってみたら、どうも著作権的にできない模様。もしくは取得対象のレイヤーが違うとか、、、

mappdf1_resized.png
▲マップ.appのメニューから書き出しを行なったPDF

c8ba8f71-2c21-4282-9e85-37af35b818cd_resized.png
▲Xcode上のAppleScriptObjCアプリケーションでMKMapViewから画像を取得して書き出しを行なったPNG

AppleScript名:指定IPアドレスの情報を取得して地図.appで表示
– Created 2017-12-10 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/5037

property NSString : a reference to current application’s NSString
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

set aInfo to getIPAddressInfo(“133.6.129.212″) of me
set aLon to (lon of (aInfo as record)) as string
set aLat to (lat of (aInfo as record)) as string

set aURL to “http://maps.apple.com/?ll=” & aLat & “,” & aLon & “&t=m”
open location aURL

–http://ip-api.com/docs/
on getIPAddressInfo(IPAddress)
  set link to “http://ip-api.com/json/” & IPAddress & “?fields=country,city,isp,org,as,mobile,proxy,message,lat,lon”
  
set curl_command to “curl “ & link
  
set jRes to do shell script curl_command
  
set jsonString to NSString’s stringWithString:jRes
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to (NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value))
  
return aJsonDict
end getIPAddressInfo

★Click Here to Open This Script 

2017/12/17 アドウェア「OSX.Pirrit」のAppleScriptコードを読んでみた

2016年4月にその存在が公表された、macOSをターゲットにしたアドウェア「OSX.Pirrit」。

一部で「AppleScriptが使われている」との話が注目を集めていますが、実物を見てみないと判断のつかないところです。

「Mac」を狙うアドウェア「OSX.Pirrit」が巧妙化–研究者が指摘
Macを狙うアドウェア「OSX.Pirrit」、マルウェアの手口を借りて拡散

実物を探しても見つかりません。OSX.Pirritについて構造やコードレベルで説明しているBlogの記事を見つけたため、そこに掲載されているコードのスクリーンショットから判断してみます。

・OSX.Pirrit Mac Adware Part III: The DaVinci Code
https://www.cybereason.com/blog/targetingedge-mac-os-x-pirrit-malware-adware-still-active

OSX.Pirritの感染経路はMac用アプリケーションのインストーラーパッケージおよびそこに仕込まれたshell scriptです。このshell scriptが管理者権限で実行されることを利用しています。「怪しいインストーラーを実行すると危険」というのが本アドウェアについてのポイントです。感染(ルート権限でやり放題にプログラムを実行)状態になってしまえば、あとは何を使って何をされても「後の祭り」です。

# プロセスのBundle IDを偽装する、という「手口」はなかなか参考になりました

まずは、shell scriptでupdaterプログラムをルート権限で定期実行できるようにしたり、Objective-Cで書かれた「updater」プログラムをダウンロード。

しばらく他のプログラムの解説が続いたあと、「どこにAppleScriptの話があるの?」という気分になった頃にようやくAppleScriptの話が出てきます。

macverという実行ファイルの中にbase64でエンコードされたAppleScriptの文字列が含まれており、これをデコードして実行するようです。

AppleScriptのコードというので、どれだけ凝った処理をやっているのかと思いきや、それほど行数はないのと、半分以上はSafariにdo javascriptコマンドで実行するJavaScriptです。

OSのバージョンや環境情報を調べての対策もやっていません。割と雑なコードです。Cocoaの機能も呼び出さないし、アプリケーションプロセスの特定にBundle IDも使いません。

JavaScript部分の処理のていねいさに比べて、ものすごく雑な感じがします。

そして、ここが大事なところなのですが……現行のSafari v10.x上ではデフォルトでAppleEvent経由でのJavaScriptの実行は禁止状態になっています。

つまり、広告情報をWebブラウザのコンテンツに突っ込む部分を(デフォルトでは)実行できない状態になっています。

もう少し何か楽しませてくれるかと思っていたのですが、実に残念です。

2017/12/16 表示中のCotEditor書類の「次」のファイルを縦書きでオープン

CotEditorで表示中のテキストファイルを、ファイルの入っているフォルダの中でファイル名の並び順で「次」にあたるファイルを縦書きでオープンするAppleScriptです。

「小説家になろう」サイトからダウンロードした小説のテキストファイル(連番入りテキストファイル)をCotEditorで縦書き表示して、順次ファイルを切り替えて読み進めるために作ったものです。

CotEditorはプレーンテキストのエディタでありながら、縦書き表示が可能で、画面表示がキレイなので「縦書きテキストビューワー」としてもよく利用しています。そして、AppleScriptからのコントロールが可能なので、さまざまな「存在しない機能」を勝手に追加できます。

cot3.png

1つのフォルダ中に連番つきのファイルのうちどれかをCotEditorでオープンしている状態で、

cot1.png

本Scriptを実行すると、書類が入っているフォルダ中のテキストファイルのファイル名を抽出してソートし、現在のファイルの「次」に該当するファイルをCotEditorでオープンします。

オープンしたら、元Windowのサイズを取得して、元Windowと同じサイズに設定して、元の書類をクローズ。「次」の書類の表示状態を縦書きに設定します。

OS側のScript Menuから実行することを前提としていますが、CotEditor内蔵のScript Menuからは実行できません(内蔵MenuだとGUI Scriptingなどが実行できないもよう)。

CotEditor自体の縦書き表示/横書き表示の切り替え機能がAppleScript側に解放されていれば、AppleScript用語辞書経由でアクセスできますが、現状では表示方向の属性の取得・変更はできないので野蛮なGUI Scripting経由で行っています。

cot2.png

AppleScript名:表示中のCotEditor書類の「次」のファイルを縦書きでオープン
– Created 2017-12-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use bPlus : script “BridgePlus”
–http://piyocast.com/as/archives/5034

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property SMSForder : a reference to current application’s SMSForder
property NSPredicate : a reference to current application’s NSPredicate
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableArray : a reference to current application’s NSMutableArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSURLIsPackageKey : a reference to current application’s NSURLIsPackageKey
property NSURLIsDirectoryKey : a reference to current application’s NSURLIsDirectoryKey
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to current application’s NSDirectoryEnumerationSkipsPackageDescendants
property NSDirectoryEnumerationSkipsSubdirectoryDescendants : a reference to current application’s NSDirectoryEnumerationSkipsSubdirectoryDescendants

load framework

tell application “CotEditor”
  set dCount to count every document
  
if dCount = 0 then return
  
  
tell front document
    set curPath to path
  end tell
  
  
tell window 1
    set aBounds to bounds
  end tell
end tell

set aPath to NSString’s stringWithString:curPath
set fileName to (aPath’s lastPathComponent()) –ファイル名
set pathExtension to aPath’s pathExtension() as string
set parentFol to (aPath’s stringByDeletingLastPathComponent()) as string —親フォルダ

–同じフォルダから同じ拡張子のファイルのファイル名を取得
set fList to my getFilesByIncludedStringInName:(pathExtension) fromDirectory:(parentFol) exceptPackages:(true)

–昇順ソート
set aArray to NSArray’s arrayWithArray:fList
set desc1 to NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:true selector:“localizedCaseInsensitiveCompare:”
set bArray to aArray’s sortedArrayUsingDescriptors:{desc1}

–ファイル名検索
set aIndex to (SMSForder’s indexesOfItem:fileName inArray:bArray inverting:false) as list
if aIndex = {} then
  display notification “Error: File Not Found”
  
return
end if

set bIndex to (contents of first item of aIndex) + 1 + 1 –0 based to 1 based conversion & next one
set aLen to length of (bArray as list)
if bIndex > aLen then
  display notification “Error: Out of bounds”
  
return
end if

set newFile to contents of item bIndex of (bArray as list)
set newPath to parentFol & “/” & newFile

tell application “CotEditor”
  set oldDoc to front document
  
  
open (POSIX file newPath) as alias
  
tell window 1
    set bounds to aBounds
  end tell
  
  
close oldDoc without saving
end tell

makeWinVertical() of me –縦書き表示

–指定フォルダ内の指定文字列を含むファイル名のlistを抽出する
on getFilesByIncludedStringInName:(fileNameStr as string) fromDirectory:(sourceFolder) exceptPackages:(packageF as boolean)
  set fileManager to NSFileManager’s defaultManager()
  
set aURL to |NSURL|’s fileURLWithPath:sourceFolder
  
set theOptions to (NSDirectoryEnumerationSkipsPackageDescendants as integer) + (NSDirectoryEnumerationSkipsHiddenFiles as integer) + (NSDirectoryEnumerationSkipsSubdirectoryDescendants as integer)
  
set directoryContents to fileManager’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value)
  
set findPredicates to NSPredicate’s predicateWithFormat_(“lastPathComponent CONTAINS %@”, fileNameStr)
  
set foundItemList to directoryContents’s filteredArrayUsingPredicate:findPredicates
  
  
–Remove Folders From found URL Array
  
set anArray to NSMutableArray’s alloc()’s init()
  
repeat with i in foundItemList
    set j to contents of i
    
set {theResult, isDirectory} to (j’s getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value))
    
    
–Collect files
    
if (isDirectory as boolean = false) then
      (anArray’s addObject:j)
    else if (packageF = false) then
      –Allow Package files?
      
set {theResult, isPackage} to (j’s getResourceValue:(reference) forKey:(NSURLIsPackageKey) |error|:(missing value))
      
if (isPackage as boolean) = true then
        (anArray’s addObject:j)
      end if
    end if
    
  end repeat
  
  
return (anArray’s valueForKey:“lastPathComponent”) as list
end getFilesByIncludedStringInName:fromDirectory:exceptPackages:

–Make CotEditor’s front window to Vertical display mode (Tategaki)
on makeWinVertical()
  activate application “CotEditor”
  
tell application “System Events”
    tell process “CotEditor”
      try
        click menu item “縦書きで表示” of menu 1 of menu bar item “フォーマット” of menu bar 1
      end try
    end tell
  end tell
end makeWinVertical

★Click Here to Open This Script 

2017/12/15 ハンドラ間接呼び出し

ハンドラを間接的に呼び出すAppleScriptです。ハンドラへの参照を変数に入れて、変数経由でハンドラを呼び出します。

さんざん実験していた内容ですが、「失敗していた」と思っていたところ成功していました。

ただし、呼び出される側のハンドラは、Pure AppleScriptでよく見られる、

 on aHandler(aParam, bParam)

のような記法では間接呼び出しは無理で、

 on aHandler given aParam:XXXX bParam:YYYY

のようにgivenでラベル付きパラメータを記述するタイプでしか呼び出せていません。

 on aHandlerWithAParam:XXXX bParam:YYYY

のようなObjective-Cスタイルのハンドラもダメです。

ただし、無意味句を用いた英文タイプのものは大丈夫だと思います。

AppleScript名:ハンドラ間接呼び出し
– Created 2017-12-14 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/5031

set a to class of b
–> handler
set d to class of c
–> handler

set bHandle to (a reference to b) as handler
set cHandle to (a reference to c) as handler

copy bHandle to indirectHandler
set e to indirectHandler given parameter:“ABC”
–> “ABC_B”

copy cHandle to indirectHandler
set e to indirectHandler given parameter:“ABC”
–> “ABC_C”

on b given parameter:thisParam as string
  return thisParam & “_B”
end b

on c given parameter:thisParam as string
  return thisParam & “_C”
end c

★Click Here to Open This Script 

2017/12/14 指定文字の花文字テキストを取得する(Retina対応)

指定文字を指定フォントの花文字を作成してCotEditor上に花文字で新規ドキュメントを作成するAppleScriptのRetina Display対応版です。

テキストエディタへの出力部分はTextEdit.app、BBEdit、TextWrangler、mi、Jedit Omega、CotEditor用のルーチンを差し替えればすぐに対応可能です。

以前に掲載したルーチンがRetina Display(144×144dpi)の環境を考慮していなかったので、対応させてみました(Retina Display搭載機をLid Closed Modeで運用して外部ディスプレイつないでいるので)。

Retina Display対応というのは、倍の解像度で文字が出力されるという話ではなく、Retina Displayの環境でも問題なく文字全体が出力されるということです。

flowtext.png

AppleScript名:指定文字の花文字テキストを取得する(Retina対応)
– Created 2017-12-12 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/5028

property NSFont : a reference to current application’s NSFont
property NSUUID : a reference to current application’s NSUUID
property NSColor : a reference to current application’s NSColor
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSBezierPath : a reference to current application’s NSBezierPath
property NSColorSpace : a reference to current application’s NSColorSpace
property NSPNGFileType : a reference to current application’s NSPNGFileType
property NSFontManager : a reference to current application’s NSFontManager
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSKernAttributeName : a reference to current application’s NSKernAttributeName
property NSMutableParagraphStyle : a reference to current application’s NSMutableParagraphStyle
property NSLigatureAttributeName : a reference to current application’s NSLigatureAttributeName
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSUnderlineStyleAttributeName : a reference to current application’s NSUnderlineStyleAttributeName
property NSParagraphStyleAttributeName : a reference to current application’s NSParagraphStyleAttributeName
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName

set aString to “あ”
set hanaSize to 36
set thisFont to selectAFont(aString) of me
if thisFont = false then return –Cancel

–花文字文字列を作成
set fRes to getHanamojiStr(hanaSize, thisFont, aString, 0.7, true) of me
makeNewDocument given parameter:fRes

–花文字文字列を計算して返す
on getHanamojiStr(aFontSize as real, aFontName as string, aString as string, aThread as real, incFontName as boolean)
  if length of aString is not equal to 1 then return false
  
  
–指定文字コードが指定フォント中に存在するかチェック
  
set fRes to retGlyphsInFont(aFontName, id of aString) of me
  
if fRes = false then return false
  
  
set aThreadShould to 768 * aThread
  
if (chkMultiByteChar(aString) of me) = false then
    set spaceChar to string id 12288 –全角スペース(UTF-16)
  else
    set spaceChar to string id 32 –半角スペース
  end if
  
  
set fillColor to NSColor’s whiteColor –塗り色
  
set bString to aString & ” “ –処理内容の帳尻合わせ(そのままだと右端が欠けるのでスペースを入れた)
  
set anAssrStr to makeRTFfromParameters(bString, aFontName, aFontSize, -2, (aFontSize * 1.2)) of me
  
set aSize to anAssrStr’s |size|()
  
  
if class of aSize = record then
    set attrStrWidth to width of aSize
    
set attrStrHeight to height of aSize
  else if class of aSize = list then –macOS 10.13.xのバグ回避
    copy aSize to {attrStrWidth, attrStrHeight}
  end if
  
  
set {xPos, yPos} to {0, 0}
  
  
set tmpImg1 to makeImageWithFilledColor(attrStrWidth, attrStrHeight, fillColor) of me
  
set tmpImg2 to drawAttributedStringsOnImage(tmpImg1, anAssrStr, xPos, yPos) of me
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:(tmpImg2’s TIFFRepresentation())
  
  
–画像から順次指定座標の色データを拾って花文字listに反映
  
set strList to {}
  
repeat with y from 1 to attrStrHeight - 1
    
    
set strListX to {}
    
repeat with x from 0 to attrStrWidth - 1
      set tmpCol to getColorFromRawImage(aRawimg, x, y) of me
      
      
if tmpCol is not equal to false then
        if tmpCol is not equal to {255, 255, 255} then
          copy tmpCol to {tmpR, tmpG, tmpB}
          
if (tmpR + tmpG + tmpB) < aThreadShould then
            set the end of strListX to aString
          else
            set the end of strListX to spaceChar
          end if
        else
          set the end of strListX to spaceChar
        end if
      end if
      
    end repeat
    
set the end of strList to strListX
  end repeat
  
  
–2D List→Text
  
set aRes to list2dToStringByUsingDelimiters(strList, “”, return) of me
  
  
if incFontName = true then
    set fName to getDisplayedNameOfFont(aFontName) of me
    
set aRes to “■” & fName & return & return & aRes
  end if
  
  
return aRes
end getHanamojiStr

–指定Raw画像中の指定座標のピクセルの色をRGBで取り出す
on getColorFromRawImage(aRawimg, x as </