Archive for the 'CotEditor' 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 テキストを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/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 

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/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 real, y as real)
  set aRatio to getImageRatio() of me –Retina Display対応
  
set origColor to (aRawimg’s colorAtX:(x * aRatio) y:(y * aRatio))
  
  
set srgbColSpace to NSColorSpace’s deviceRGBColorSpace
  
if srgbColSpace = missing value then return false
  
  
set aColor to (origColor’s colorUsingColorSpace:srgbColSpace)
  
  
set aRed to (aColor’s redComponent()) * 255
  
set aGreen to (aColor’s greenComponent()) * 255
  
set aBlue to (aColor’s blueComponent()) * 255
  
  
return {aRed as integer, aGreen as integer, aBlue as integer}
end getColorFromRawImage

–画像のうえに指定のスタイル付きテキストを描画して画像を返す
on drawAttributedStringsOnImage(anImage, anAssrStr, xPos as real, yPos as real)
  anImage’s lockFocus()
  
anAssrStr’s drawAtPoint:(current application’s NSMakePoint(xPos, yPos))
  
anImage’s unlockFocus()
  
return anImage
end drawAttributedStringsOnImage

–書式つきテキストを組み立てる
on makeRTFfromParameters(aStr as string, fontName as string, aFontSize as real, aKerning as real, aLineSpacing as real)
  set aVal1 to NSFont’s fontWithName:fontName |size|:aFontSize
  
set aKey1 to (NSFontAttributeName)
  
  
set aVal2 to NSColor’s blackColor()
  
set aKey2 to (NSForegroundColorAttributeName)
  
  
set aVal3 to aKerning
  
set akey3 to (NSKernAttributeName)
  
  
set aVal4 to 0
  
set akey4 to (NSUnderlineStyleAttributeName)
  
  
set aVal5 to 2 –all ligature ON
  
set akey5 to (NSLigatureAttributeName)
  
  
set aParagraphStyle to NSMutableParagraphStyle’s alloc()’s init()
  
aParagraphStyle’s setMinimumLineHeight:(aLineSpacing)
  
aParagraphStyle’s setMaximumLineHeight:(aLineSpacing)
  
set akey7 to (NSParagraphStyleAttributeName)
  
  
set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey7}
  
set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aParagraphStyle}
  
set attrsDictionary to NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
  
  
set attrStr to NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
  
return attrStr
end makeRTFfromParameters

–指定サイズの画像を作成し、背景を指定色で塗る
on makeImageWithFilledColor(aWidth as real, aHeight as real, fillColor)
  set anImage to NSImage’s alloc()’s initWithSize:(current application’s NSMakeSize(aWidth, aHeight))
  
set aRatio to getImageRatio() of me –Retina Display対応
  
  
anImage’s lockFocus()
  
set theRect to {{x:0, y:0}, {width:aWidth * aRatio, height:aHeight * aRatio}}
  
set theNSBezierPath to NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  
fillColor’s |set|()
  
theNSBezierPath’s fill()
  
anImage’s unlockFocus()
  
  
return anImage
end makeImageWithFilledColor

–2D Listをアイテム間および行間のデリミタを個別に指定してテキスト変換
on list2dToStringByUsingDelimiters(aList as list, itemDelimiter, lineDelimiter)
  set outList to {}
  
repeat with i in aList
    set aStr to listToStringUsingTextItemDelimiter(i, itemDelimiter) of me
    
set the end of outList to aStr
  end repeat
  
  
set bStr to listToStringUsingTextItemDelimiter(outList, lineDelimiter) of me
  
return bStr
end list2dToStringByUsingDelimiters

on listToStringUsingTextItemDelimiter(sourceList as list, textItemDelimiter)
  set CocoaArray to NSArray’s arrayWithArray:sourceList
  
set CocoaString to CocoaArray’s componentsJoinedByString:textItemDelimiter
  
return (CocoaString as string)
end listToStringUsingTextItemDelimiter

–ユーザー環境にインストールされているすべてのフォントのPostScript名とグリフ数を返す
on getEveryFontPSNameANdGlyphsNum(aStr)
  set aFontList to NSFontManager’s sharedFontManager()’s availableFonts()
  
set thePred to NSPredicate’s predicateWithFormat:“NOT SELF BEGINSWITH ’.’”
  
set aFontList to (aFontList’s filteredArrayUsingPredicate:thePred) as list
  
  
set aList to {}
  
repeat with i in aFontList
    set aName to contents of i
    
set aNum to countNumberOfGlyphsInFont(aName) of me
    
set dName to getDisplayedNameOfFont(aName) of me
    
    
set fRes to retGlyphsInFont(aName, id of aStr) of me
    
if fRes = true then
      set the end of aList to {fontName:aName, fontNum:aNum, dispName:dName}
    end if
  end repeat
  
  
return aList
end getEveryFontPSNameANdGlyphsNum

–指定Postscript名称のフォントに定義されている文字数を数えて返す
on countNumberOfGlyphsInFont(fontName as string)
  set aFont to NSFont’s fontWithName:fontName |size|:9.0
  
if aFont = missing value then return false
  
set aProp to aFont’s numberOfGlyphs()
  
return aProp as number
end countNumberOfGlyphsInFont

–フォントのPostScript NameからDisplayed Nameを取得
on getDisplayedNameOfFont(aName as string)
  set aFont to NSFont’s fontWithName:aName |size|:9.0
  
set aDispName to (aFont’s displayName()) as string
  
return aDispName
end getDisplayedNameOfFont

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

–指定名称のフォントに指定の文字コードが含まれているかチェック
on retGlyphsInFont(fontName as string, strCode as integer)
  set aFont to NSFont’s fontWithName:fontName |size|:24.0
  
if aFont = missing value then return false
  
set aSet to aFont’s coveredCharacterSet()
  
set aRes to (aSet’s characterIsMember:strCode) as boolean
  
return aRes as list of string or string –as anything
end retGlyphsInFont

on selectAFont(aString)
  set fRes to getEveryFontPSNameANdGlyphsNum(aString) of me
  
set theArray to NSArray’s arrayWithArray:fRes
  
set thePred to NSPredicate’s predicateWithFormat:“fontNum > 10000″
  
set bArray to ((theArray’s filteredArrayUsingPredicate:thePred)’s valueForKeyPath:“dispName”) as list
  
set thisFont to choose from list bArray
  
return thisFont
end selectAFont

–Retina Display対応ルーチン
on getImageRatio()
  set retinaF to detectRetinaDisplay() of me
  
if retinaF = true then
    return 2.0 as real
  else
    return 1.0 as real
  end if
end getImageRatio

on detectRetinaDisplay()
  set dispList to current application’s NSScreen’s screens()
  
set retinaF to false
  
  
repeat with i in dispList
    set j to contents of i
    
set aDepth to j’s backingScaleFactor()
    
if aDepth > 1.0 then
      set retinaF to true
    end if
  end repeat
  
  
return retinaF
end detectRetinaDisplay

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

★Click Here to Open This Script 

2017/12/13 ハンドラの間接呼び出しのじっけん

ハンドラの間接呼び出しの実験を行うAppleScriptです。

variableeditor.gif

ハンドラの間接呼び出しは、Scripter誰もが一度は考えてみる課題ですが、自分にはどう考えてもできませんでした。もしかして、世界にひとりぐらいは実現している人間がいるかもしれませんが、とりあえず自分の結論は「無理」というものでした。

# recordを文字列に変換するのも、当初は「言語仕様的に不可能」と言われた空前絶後の超絶テクニックでしたが、いまでは割と手垢のついた「手口」に

ハンドラの存在確認が行える以上、間接的にハンドラを呼び出すことも不可能ではなさそうな雰囲気もしていますが、とりあえず行えていません。

結局、異なるAppleScript Librariesに同じ名前のハンドラを作っておき、呼び出し先のAppleScript Librariesを「代入」で切り替えることで、間接呼び出しっぽい処理を実現してみました。

scriptlibs.png

別にif文で条件分岐してもかまわなさそうな内容ですが、「やってできないことはない」という落とし所が見えました。

本Scriptでは、TextEdit.app、BBEdit、TextWranglermiJedit OmegaCotEditorという6本のテキストエディタをダイアログで選択し、当該エディタで花文字テキストの新規ドキュメントを作成しています。

–> Download This Script Bundle

AppleScript名:variableHandlerTest
– Created 2017-12-12 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use ed001 : script “textedit”
use ed002 : script “bbedit”
use ed003 : script “textwrangler”
use ed004 : script “mi”
use ed005 : script “jeditomega”
use ed006 : script “coteditor”
–http://piyocast.com/as/archives/5023

set aString to retFlowerText() of me

set appIDlist to {“com.apple.textedit”, “com.barebones.bbedit”, “com.barebones.textwrangler”, “net.mimikaki.mi”, “jp.co.artman21.JeditOmega”, “com.coteditor.CotEditor”}
set scriptObjList to {ed001, ed002, ed003, ed004, ed005, ed006}

set appNameList to {}
set appBundleIDList to {}

repeat with i in appIDlist
  set anAppID to contents of i
  
set aRes to checkAppInstallation(anAppID) of me
  
if aRes is not equal to false then
    set the end of appBundleIDList to anAppID
  end if
end repeat

set cRes to choose from list appBundleIDList
if cRes = false then return –Cancel

set appIDnum to retIndexNumInArray(appIDlist, contents of first item of cRes)
set objRef to contents of item appIDnum of scriptObjList

copy objRef to theTargetEditor –Very Important!!

tell theTargetEditor
  makeNewDocument given parameter:aString
end tell

on retFlowerText()
  return “                                            
           あああああ                            
           あああああ                            
           あああああ                            
           あああああ                            
    ああああああああああああああああああああああああああああ            
    ああああああああああああああああああああああああああああ            
    ああああああああああああああああああああああああああああ            
    ああああああああああああああああああああああああああああ            
           あああああ                            
           あああああ                            
           あああああ  ああああああ                    
           あああああああああああああああああ                
           あああああああああああああああああああ              
         あああああああああ   ああああああああああ             
        ああああああああ    ああああ  ああああああ            
       あああああああああ    ああああ   あああああ            
      あああああ ああああ   あああああ    あああああ           
     あああああ  あああああ  ああああ     あああああ           
     ああああ   あああああ あああああ     あああああ           
    あああああ   ああああああああああ      あああああ           
    ああああ     あああああああああ      あああああ           
   あああああ     ああああああああ       あああああ           
   あああああ     あああああああ        あああああ           
   あああああ     ああああああ        ああああああ           
   あああああ    ああああああ         ああああああ           
   ああああああああああああああ        あああああああ            
    ああああああああああああ       ああああああああ             
    あああああああああああ   あああああああああああああ             
     ああああああああ     あああああああああああ               
        あ         あああああああああ                 
                                            
                                            

end retFlowerText

–指定IDのアプリケーションがHDD上に存在するかを確認
on checkAppInstallation(appID)
  tell application “Finder”
    try
      set aRes to exists of application file id appID
    on error
      return false
    end try
  end tell
  
  
return true
end checkAppInstallation

–1D List中のシーケンシャルサーチ
on retIndexNumInArray(aList, aTarget)
  script obj
    property list : aList
  end script
  
  
set aCount to 1
  
set hitF to false
  
  
repeat with i in obj’s list
    set j to contents of i
    
if aTarget = j then
      return aCount
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
if hitF = false then return 0
end retIndexNumInArray

★Click Here to Open This Script 

AppleScript名:textedit
on makeNewDocument given parameter:aStr
  tell application id “com.apple.textedit”
    set aDoc to make new document
    
tell aDoc
      set text of it to aStr
    end tell
    
activate
  end tell
end makeNewDocument

★Click Here to Open This Script 

AppleScript名:bbedit
on makeNewDocument given parameter:aStr
  tell application id “com.barebones.bbedit”
    set aDoc to make new text document with properties {text:aStr}
    
activate
  end tell
end makeNewDocument

★Click Here to Open This Script 

AppleScript名:textwrangler
on makeNewDocument given parameter:aStr
  tell application id “com.barebones.textwrangler”
    set aDoc to make new text document with properties {text:aStr}
    
activate
  end tell
end makeNewDocument

★Click Here to Open This Script 

AppleScript名:mi
on makeNewDocument given parameter:aStr
  tell application id “net.mimikaki.mi”
    make new document with properties {content:aStr}
    
activate
  end tell
end makeNewDocument

★Click Here to Open This Script 

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

★Click Here to Open This Script 

AppleScript名:coteditor
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

★Click Here to Open This Script 

2017/11/18 指定文字の花文字を取得 v1.2

OS内にインストールされているフォントのうち収録グリフ数が8,000以上のフォントから50個をリストアップして、各フォントで花文字を作成してCotEditor上に花文字で新規ドキュメントを作成するAppleScriptです。

注意:ただし、本ScriptはRetina Display未対応です

flowerchar3_resized.png

フォント50個に限定しているのは、大量にCotEditorでドキュメントを作成するとCotEditorのパフォーマンスが大幅に低下するためです(メモリの都合? 常識的な挙動なので問題ではありません。ドキュメントの開きすぎで)。

なお、OS内にインストールされているフォントの数が極端に少ない環境(条件に合致するフォントが50個ないとか)ではエラーになる可能性があります。

flowerchar2_resized.png

CotEditor上に50個の未保存のドキュメントができてしまうので、一括で破棄するAppleScriptを掲載しておきます。

2017-11-18-22_12_04.gif

tell application “CotEditor”
  tell every document
    close without saving
  end tell
end tell

★Click Here to Open This Script 

AppleScript名:指定文字の花文字を取得 v1.2
– Created 2017-11-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4984

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

set aString to “ぴ”

set fRes to getEveryFontPSNameANdGlyphsNum() of me
set theArray to current application’s NSArray’s arrayWithArray:fRes
set thePred to current application’s NSPredicate’s predicateWithFormat:“fontNum > 8000″
set bArray to (theArray’s filteredArrayUsingPredicate:thePred) as list
set cArray to items 1 thru 50 of bArray

repeat with i in cArray
  set aFontName to contents of i
  
set fRes to getHanamojiStr(18, fontName of aFontName, aString, 0.7, true) of me
  
makeNewCotEditorDoc(fRes) of me
end repeat

–花文字文字列を計算して返す
on getHanamojiStr(aFontSize, aFontName, aString, aThread, incFontName)
  if length of aString is not equal to 1 then return false
  
  
set aThreadShould to 768 * aThread
  
  
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
  
  
–下地の画像の上にAttributed Stringを描画
  
set tmpImg2 to drawAttributedStringsOnImage(tmpImg1, anAssrStr, xPos, yPos) of me
  
  
–NSImageからRaw画像を作成
  
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 “ ”
          end if
        else
          set the end of strListX to “ ”
        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, y)
  set origColor to (aRawimg’s colorAtX:x y:y)
  
set srgbColSpace to NSColorSpace’s deviceRGBColorSpace
  
if srgbColSpace = missing value then return false
  
  
set aColor to (origColor’s colorUsingColorSpace:srgbColSpace)
  
  
set aRed to (aColor’s redComponent()) * 255
  
set aGreen to (aColor’s greenComponent()) * 255
  
set aBlue to (aColor’s blueComponent()) * 255
  
  
return {aRed as integer, aGreen as integer, aBlue as integer}
end getColorFromRawImage

–画像のうえに指定のスタイル付きテキストを描画して画像を返す
on drawAttributedStringsOnImage(anImage, anAssrStr, xPos, yPos)
  anImage’s lockFocus()
  
anAssrStr’s drawAtPoint:(current application’s NSMakePoint(xPos, yPos))
  
anImage’s unlockFocus()
  
return anImage
end drawAttributedStringsOnImage

–書式つきテキストを組み立てる
on makeRTFfromParameters(aStr as string, fontName as string, aFontSize as real, aKerning as real, aLineSpacing as real)
  set aVal1 to NSFont’s fontWithName:fontName |size|:aFontSize
  
set aKey1 to (NSFontAttributeName)
  
  
set aVal2 to NSColor’s blackColor()
  
set aKey2 to (NSForegroundColorAttributeName)
  
  
set aVal3 to aKerning
  
set akey3 to (NSKernAttributeName)
  
  
set aVal4 to 0
  
set akey4 to (NSUnderlineStyleAttributeName)
  
  
set aVal5 to 2 –all ligature ON
  
set akey5 to (NSLigatureAttributeName)
  
  
set aParagraphStyle to NSMutableParagraphStyle’s alloc()’s init()
  
aParagraphStyle’s setMinimumLineHeight:(aLineSpacing)
  
aParagraphStyle’s setMaximumLineHeight:(aLineSpacing)
  
set akey7 to (NSParagraphStyleAttributeName)
  
  
set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey7}
  
set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aParagraphStyle}
  
set attrsDictionary to NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
  
  
set attrStr to NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
  
return attrStr
end makeRTFfromParameters

–指定サイズの画像を作成し、背景を指定色で塗る
on makeImageWithFilledColor(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}, {width:aWidth, height:aHeight}}
  
set theNSBezierPath to NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  
fillColor’s |set|()
  
theNSBezierPath’s fill()
  
anImage’s unlockFocus()
  
  
return anImage
end makeImageWithFilledColor

–2D Listをアイテム間および行間のデリミタを個別に指定してテキスト変換
on list2dToStringByUsingDelimiters(aList, itemDelimiter, lineDelimiter)
  set outList to {}
  
repeat with i in aList
    set aStr to listToStringUsingTextItemDelimiter(i, itemDelimiter) of me
    
set the end of outList to aStr
  end repeat
  
  
set bStr to listToStringUsingTextItemDelimiter(outList, lineDelimiter) of me
  
return bStr
end list2dToStringByUsingDelimiters

on listToStringUsingTextItemDelimiter(sourceList, textItemDelimiter)
  set CocoaArray to NSArray’s arrayWithArray:sourceList
  
set CocoaString to CocoaArray’s componentsJoinedByString:textItemDelimiter
  
return (CocoaString as string)
end listToStringUsingTextItemDelimiter

on getEveryFontPSNameANdGlyphsNum()
  set aFontList to NSFontManager’s sharedFontManager()’s availableFonts()
  
set thePred to NSPredicate’s predicateWithFormat:“NOT SELF BEGINSWITH ’.’”
  
set aFontList to (aFontList’s filteredArrayUsingPredicate:thePred) as list
  
  
set aList to {}
  
repeat with i in aFontList
    set aName to contents of i
    
set aNum to countNumberOfGlyphsInFont(aName) of me
    
set the end of aList to {fontName:aName, fontNum:aNum}
  end repeat
  
  
return aList
end getEveryFontPSNameANdGlyphsNum

–指定Postscript名称のフォントに定義されている文字数を数えて返す
on countNumberOfGlyphsInFont(fontName)
  set aFont to current application’s NSFont’s fontWithName:fontName |size|:9.0
  
if aFont = missing value then return false
  
set aProp to aFont’s numberOfGlyphs()
  
return aProp
end countNumberOfGlyphsInFont

–フォントのPostScript NameからDisplayed Nameを取得
on getDisplayedNameOfFont(aName)
  set aFont to current application’s NSFont’s fontWithName:aName |size|:9.0
  
set aDispName to (aFont’s displayName()) as string
  
return aDispName
end getDisplayedNameOfFont

on makeNewCotEditorDoc(aCon)
  tell application “CotEditor”
    set newDoc to make new document
    
tell newDoc
      set contents to aCon
    end tell
  end tell
end makeNewCotEditorDoc

★Click Here to Open This Script 

2017/10/22 CotEditorのコンソールにログ出力

CotEditor内蔵のコンソールウィンドウに任意の文字列を出力するAppleScriptです。

CotEditorの「ウィンドウ」メニューに「パネル」という項目があり、

cot21.png

ここで「コンソールパネル」が選択でき、フローティングパレットが表示されます(CotEditor v3.2.2にて確認)。

cot1.png

このパレットが用意された意図や経緯はわかりませんが(syslog経由でConsole.appに出せるのに)、おそらく内蔵Script Menuから実行する各種scriptのデバッグ出力のログ表示用と思われます(10:88ってなんだ???)。

CotEditorのAppleScript用語辞書に「write to console」というコマンドが用意されており、指定の文字列をこのコンソールパネルに出力できます。

cot3.png

このコンソール出力のためのAppleScriptを書いてためしてみました。テキストを出力することはできますが、リスト(配列)やレコード(dictionary)を出力することはできません。テキスト以外の形式のデータはテキストに変換して出力することになります。

なお、「コンソールパネル」はフローティングパレットUIなので、CotEditorが最前面にある時にしか表示されません。

辞書にあっても実際に機能するか、期待どおりに機能するかはやはり実際に試してみないとわかりません。

AppleScript名:CotEditorのコンソールにログ出力
–http://piyocast.com/as/archives/4918
tell application “CotEditor”
  activate
  
write to console “ぴよまるさんだよ”
end tell

★Click Here to Open This Script 

2017/10/05 クリップボード内のZero Width Spaceを削除する

クリップボードから、Cocoaの機能を使うと削除も置換もできないZero Width Spaceを削除するAppleScriptです。

zerowidthspace.png
ScriptableなUnicode文字情報チェックツール「UnicodeChecker」

特定のエディタ(ASObjCExplorer)が出力するものの、Cocoaの機能を使うと削除や置換ができないために、Objective-CやSwiftだけで何も対策しないで組んであると編集自体が行えないという恐怖のキャラクターZero Width Space。

各テキストエディタのZero Width Spaceへの対応度はまちまちで、

TextWranler(=BBEdit):表示および削除が可能
tetwrangler.png

CotEditor:ちょっと前まで認識・表示しなかった。最新版(v3.2.2)では表示・削除ができるようになった
coteditor.png

mi:表示できないが、Zero Width Spaceがある場所で不自然にカーソルが進まなくなるので、存在は確認できる。マウスで前後の文字ごとまとめて範囲選択して削除することは可能

といった状況です。実際にCocoaのAPIを用いて文字置換するとぜんぜんダメなので、Pure AppleScriptの機能を用いて組んでいます。

自分がMac App Storeで販売中のPDF差分検出アプリケーション「Double PDF」でもこのZero Width Space対策を行なっており、本文テキスト同士の比較時にはあらかじめ削除するようにしています。

scrit_menu_zero.png

本Scriptは利用頻度が妙に高いので、Script Menuに入れて呼び出すような運用を行っています。クリップボードにZero Width Space駆除対象文字列を入れて(コピーして)本Scriptを実行すると、クリップボード内からZero Width Spaceが駆除されます。

AppleScript名:クリップボード内のZero Width Spaceを削除する
– Created 2017-10-05 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4881
set aText to the clipboard
set bText to aText as string
set cText to repChar(bText, string id 8203, “”) of me
set the clipboard to (cText as string)

–文字置換
on repChar(origText as string, targChar as string, repChar as string)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

★Click Here to Open This Script 

AppleScript名:Zero Width Spaceのプロパティを取得
–http://piyocast.com/as/archives/4881
tell application “UnicodeChecker”
  properties of code point (8203 + 1)
end tell
–> {bidi mirrored:false, containing plane:plane id 0 of application “UnicodeChecker”, id:8203, line break:”ZW”, assigned:true, canonical combining class description:”Not_Reordered”, unicode name:”ZERO WIDTH SPACE”, assigned to abstract character:true, code point type:Format, class:code point, bidi class description:”Boundary_Neutral”, script name:”Common”, general category description:”Format”, bidi class:”BN”, containing block:block “General Punctuation” of application “UnicodeChecker”, general category:”Cf”, name:”", canonical combining class:0}

★Click Here to Open This Script 

2017/07/19 CotEditor開発者の1024jpさんをTMUG例会にお呼びします

毎月第2土曜日、目黒で例会を行なっている「東京Macintosh Users Group」(以下、TMUG)の8月度の例会(8/12)にCotEditor開発者の1024jpさんをお呼びして、CotEditorについてお話しを聞く機会をご用意しました。

日時:8月12日(土)14:00〜17:00(14時開場、14:30開始)
場所:目黒区民センター内中小企業センター2階会議室
定員:48名

一般の方でも、TMUGホームページの登録フォームからお申し込みいただければ無料で参加していただけます。途中、ロボットよけのために、

 合い言葉:ローマ字の小文字で「川」と入れてください

とありますが、ここは「kawa」と入力してください。

CotEditorはオープンソースで開発が行われており、かつメインの開発者が入れ替わって続いてきたという珍しいプロジェクトでもあります。

2011/10/16 CotEditorをAppleScriptから操作する3

オープンソースのテキストエディタ「CotEditor」をAppleScriptから操作するシリーズの第3弾。あくまで基礎的な部分を地道に検証することが重要なので、AppleScriptで非常によく使う「selection」(選択部分)について調べてみました。

CotEditor上で、このように選択した状態で、選択部分のプロパティを取得して、その詳細を調べてみることにします。

cot40.jpg

selectionのプロパティはこのようにして取得できます。

スクリプト名:CotEditorでselectionのプロパティを取得する0
tell application “CotEditor”
  tell document 1
    set aSel to properties of selection
  end tell
end tell
–>

(*
{line range:{1, 2}, class:selection-object, character range:{0, 16}, contents:”てすとだよ
日本語を打つてすと。”}
*)

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

問題はここからです。

まずは、line rangeを取り出してみます。これは、選択部分の行の範囲を取得するというものです。

スクリプト名:CotEditorでselectionのプロパティを取得する1
tell application “CotEditor”
  tell document 1
    set aSel to properties of selection
    
set lineRange to line range of aSel
    
–> {1, 2}
    
  end tell
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

次に、character rangeを取得してみましょう。選択部分が何文字目から何文字目までなのかを取得できるはずです。

が……

スクリプト名:CotEditorでselectionのプロパティを取得する2
tell application “CotEditor”
  tell document 1
    set aSel to properties of selection
    
set charRange to character range of aSel
    
  end tell
end tell

–> error “CotEditor でエラーが起きました:range of document 1 を取り出すことはできません。” number -1728 from range of document 1

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

「character range」という予約語がきちんとAppleScriptの処理系に認識されていないようです。そのために、エラーが発生してしまいます。

真面目にcharacter rangeという予約語を生かせる方向に努力するか、あるいは先人(マイクロソフトとかアドビとか)にならって「characterRange」などという、微妙に既存の予約語と重ならないような予約語を作ってみるといいんじゃないでしょうか。

現状のままだと、比較的初心者(中級者ぐらいでも)にとっては、エラーの所在がOSにあるのか、AppleScriptの処理系にあるのか、アプリケーションに存在するのかがまるっきり分かりません。OSは動いているし、アプリケーションも普通に動作していれば、AppleScriptに問題があると考えるのが普通です。ところが、アプリケーション側の対応が不適切なケースが割と多いのが実際のところです。

「AppleScriptが分からない」というつまづきは、こうしたアプリケーション側の「ダメな実装」にも原因が(かなり)あるわけで、古くはCodeWarriorがデフォルトで出力していたAppleScript用語辞書(select tell targetという予約語が入っているのが目印)が、Script対応でないのに辞書が含まれるためAppleScript初心者を惑わせたという歴史的経緯があります。

世界的に有名な(たぶん、世界一)バカ実装では、アドビのIllustrator CS2で、色のR(赤)、G(緑)、B(青)のチャネルを示す属性に「r」「g」「b」という1文字の予約語を割り当てたというものがあります。1文字の変数と衝突して、なかなか問題の所在が分かりませんでした。さすがにこれはCS3で直りましたが、アドビの独創性豊かなバカ実装は、Appleといい勝負だと思います。

最後に、選択部分の内容(contents)を求めてみましょう。

スクリプト名:CotEditorでselectionのプロパティを取得する3
tell application “CotEditor”
  tell document 1
    set aSel to properties of selection
    
set aCon to contents of aSel
    
  end tell
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

さすがにこの部分にバグは確認されませんでした。

この確認は、単に「辞書どおりに書いて実行できるかどうか」というレベルの確認であり、長期間連続して処理をするとアプリケーションがクラッシュするとか、そういうレベルの確認ではありません(Photoshop CS3で3日間、5000回ぐらい巨大なJPEGファイルをオープン/クローズしてクラッシュしないか確認したことがありました)。

また、データサイズが小さいときの挙動と大きいときの挙動が違ったり、アプリケーションによってはドキュメントオープン後に内部の状態が安定するまでに時間がかかるようなケースもあるので、そのあたりの様子を見ながらScriptを書くことが重要です。

2011/10/12 CotEditorをAppleScriptから操作する2

オープンソースのテキストエディタ「CotEditor」をAppleScriptから操作するシリーズの続編です。

基礎的な命令を試してみます。documentのプロパティを取得。

スクリプト名:CotEditorでdocumentのプロパティを取得する
tell application “CotEditor”
  tell document 1
    properties
  end tell
end tell
–>

(*
{line ending:LF, line spacing:0.0, coloring style:”なし”, modified:true, alpha only textView:true, transparency:1.0, contents:”てすとだよ
日本語を打つてすと。
Abcdefgh Ijklmn Opqrstu”, name:”名称未設定”, wrap lines:true, class:document, path:missing value, text:”てすとだよ
日本語を打つてすと。
Abcdefgh Ijklmn Opqrstu”, IANA charset:”utf-8″, encoding:”Unicode(UTF-8)”, length:40}
*)

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

いろいろと面白そうなプロパティがあります。では、ウィンドウの透明度を変えてみましょう。

スクリプト名:CotEditorでウィンドウの透明度を変更する1
tell application “CotEditor”
  tell document 1
    set transparency to 1
  end tell
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

このScriptを実行した状態が以下の画面です。

cot21.jpg

ここから、

スクリプト名:CotEditorでウィンドウの透明度を変更する2
tell application “CotEditor”
  tell document 1
    set transparency to 0.5
  end tell
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

を実行すると、

cot22.jpg

のように、Script中で指定したとおり透明度が0.5(1.0が完全不透明状態、0が透明状態)になります。

ただし、この透明度の設定が環境設定値と連動していないようで、

cot23.jpg

(一度AppleScriptから透明度の変更を行うと)環境設定の値を変更しても、画面表示は変わりません。また、AppleScriptから設定した透明度がこの環境設定には反映されません。

2011/10/10 CotEditorをAppleScriptから操作する

日本人の手によるオープンソース・アプリケーション「CotEditor」のバージョン1.2をAppleScriptから操作してみました。

以前から存在は知っていたものの、デザインなどのフィーリングが合わなかったので常用するには到っていませんでした。

co1.jpg
▲TextWrangler

co2.jpg
▲mi

co3.jpg
▲CotEditor

AppleScriptの用語辞書を見てみたところ、

cot00.jpg

ちょっと面白そうな命令が並んでいたので、どのぐらいの実装レベルなのか試してみることにしました。

基本的なところから、アプリケーションにプロパティをくれるよう依頼してみると……常識的なレベルの動作は行っているようです。

スクリプト名:CotEditorのアプリケーションのプロパティを取得
tell application “CotEditor”
  properties
end tell
–> {name:”CotEditor”, frontmost:false, class:application, version:”1.2″}

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

次に、ドキュメントを1枚表示した状態で、Windowの枚数をカウント。1ドキュメントしか開いていないのにWindowの数が「8」と返ってきました。何か暗雲漂う雰囲気です。おかしな動作を起こしそうな予感がします。

スクリプト名:CotEditorでWindowの枚数をカウント
tell application “CotEditor”
  set a to count every window
end tell
–> 8

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

ためしに、すべてのウィンドウに対してvisibleの設定を行ってみたところ…………

cot01.jpg

スクリプト名:CotEditorでWindowのvisibleを書き換える
tell application “CotEditor”
  activate
  
  
set aList to every window
  
repeat with i in aList
    tell i
      set visible to true
    end tell
  end repeat
  
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

予想どおり、補助パレットからドロワーから何からすべて表示状態になってしまいました。

cot02.jpg

慌てて、すべてのウィンドウのvisible属性をfalseにして、一度終了させて再度起動…………すると、今度はドキュメントのウィンドウも何も表示されなくなってしまいました。

スクリプト名:CotEditorでドキュメント関連のWindowのみvisibleにする
tell application “CotEditor”
  set wList to every window
  
repeat with i in wList
    set aProp to properties of i
    
set aDoc to document of aProp
    
    
if aDoc is not equal to missing value then
      set visible of i to true
      
set zoomed of i to true
    end if
  end repeat
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

こんな(↑)感じでしばし試行錯誤して、結局preferenceファイルを削除して再度立ち上げることで回復しましたが、ちょっとアプリケーション起動時の初期化動作が甘いのと、関係ないウィンドウをAppleScriptの操作対象にしているのが問題であるように思われました。

ウィンドウのvisible制御は実戦的なAppleScriptではよくやる話なので、visible属性をいじくると異常動作を行うレベルだと、本気のAppleScriptは組めない感じがします。