Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

タグ: NSArray

構文色分け情報を取得(Color Spaceを考慮)

Posted on 7月 4, 2019 by Takaaki Naganoya

macOS標準搭載のスクリプトエディタで定義している、AppleScriptの構文要素ごとの色分け情報。これを設定ファイルから取り出してレコード型データ(を入れた配列)に変換するAppleScriptの、カラースペースを考慮したデータの取り出しを行う改修版です。

AppleScriptの構文色分け情報は、ユーザーごとに異なります。一応、OSデフォルトの趣味の悪い配色なんかもありますが、

 (1)美的センスがなく見た目に気持ち悪い(ほら変更したいでしょ? 自分の好きなように変えてね、というメッセージだと受け止めました)

 (2)重複した色が設定されている構文要素がたくさんある(構文要素ごとの見分けがしにくいので、デフォルトのままだと無意味。ほら変更したくなったでしょ、というメッセージだと受け止めました)

ということから、AppleScript入門初日に設定色を変更。何度か細かい変更を行いつつも、今日までだいたい同じ感じの設定を使い続けています。

他のScripterがどのような配色にしているかはあまり知りません。Scripterごとに違うんだろうな、ぐらいの感じです。

ところが、蓋を開けてみたらこの構文色分け設定に「RGB以外の色」が設定できることが判明。けっこう長い間、この処理系使い続けてきましたけれど、昨日はじめて知りました。

具体的に言えば、RGB以外の「CMYK色」と「グレースケール色」の2つを設定可能なことが判明。CMYK色は、何度かアクセスしているうちに勝手にRGB色に置き換わってしまうようですが、グレースケールで指定した色はそのまま色種別がグレースケールのまま保持される(RGBに変換されたりしない)ようです。そのため、NSColorからredValue()などのメソッドで数値を取り出そうとするとエラーになる事例が発生したのでした。

そこで、RGBの各チャンネルの値を取り出す前にNSColorの色空間(グレースケール色空間というのはないので、種別ぐらい?)をチェックし、RGB以外であれば変換処理を行うようにしてみました。

ただし、この色空間(色モデル)の確認にNSColorのcolorSpaceNameというメソッドを使っている点に注意が必要です。これはmacOS 10.13で非推奨(deprecated)になっています。ヘッダーファイル上の説明を読むかぎりだと、Color Space(色空間)といいながら実際には色空間に該当する(HSBとかL*a*b*とか)ものを指定しているわけではないので、色タイプと言い換えることにした、という説明が行われていました。

このため、macOS 10.15betaでは未確認ではあるものの、このNSColorからRGB値を取り出すという処理自体を書き換える必要が出てくるため、本ルーチンだけ別のライブラリに分離しておくとか、メンテナンス時にわかるように書いておくとよいでしょう(NSColorのtypeを取得してみたものの、いまひとつサンプルなどもなく不明。macOS 10.14上で全部一律で0が返ってくるし)。

AppleにOS内部の機能修正リクエストを出す場合、「実際に機能不全を起こしているんだから直してほしい」と依頼してもなかなか直らない一方で、「学術的に正しくない」という指摘をされるととっとと変更するんだなーという発見をしたような気がします。

AppleScript名:構文色分け情報を取得(Color Spaceを考慮).scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/04
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSDictionary : a reference to current application’s NSDictionary
property NSUnarchiver : a reference to current application’s NSUnarchiver

set cList to getAppleScriptSourceColors() of me

–AppleScriptの構文色分けのカラー値をRGBで取得する
on getAppleScriptSourceColors()
  — get the plist info as a dictionary
  
set thePath to NSString’s stringWithString:"~/Library/Preferences/com.apple.applescript.plist"
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theInfo to NSDictionary’s dictionaryWithContentsOfFile:thePath
  
  
— extract relevant part and loop through
  
set theArray to (theInfo’s valueForKey:"AppleScriptSourceAttributes") as list
  
  
set colList to {}
  
  
repeat with i from 1 to count of theArray
    set anEntry to item i of theArray
    
    
set colorData to NSColor of anEntry
    
set theColor to (NSUnarchiver’s unarchiveObjectWithData:colorData)
    
    
set {rVal, gVal, bVal} to retColListFromNSColor(theColor, 255) of me
    
    
set fontData to NSFont of anEntry
    
set theFont to (NSUnarchiver’s unarchiveObjectWithData:fontData)
    
    
set aFontName to theFont’s displayName() as text
    
set aFontSize to theFont’s pointSize()
    
    
set aColRec to {redValue:rVal, greenValue:gVal, blueValue:bVal, fontName:aFontName, fontSize:aFontSize}
    
    
set the end of colList to aColRec
  end repeat
  
  
return colList
end getAppleScriptSourceColors

–NSColorからRGBの値を取り出す
on retColListFromNSColor(aCol, aMAX as integer)
  set aSpace to (aCol’s colorSpaceName()) as string
  
  
if aSpace is in {"NSDeviceRGBColorSpace", "NSCalibratedRGBColorSpace"} then
    –do nothing
  else
    –RGB以外の色空間の色は、RGBに変換
    
–CMYKとグレースケールは同一メソッドでRGBに変換できることを確認済み
    
set cCol to aCol’s colorUsingColorSpaceName:"NSCalibratedRGBColorSpace"
    
if cCol = missing value then error "Color Space Conversion Error (" & aSpace & " to NSCalibratedRGBColorSpace)"
    
copy cCol to aCol
  end if
  
  
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 

Posted in Color list Record System | Tagged 10.12savvy 10.13savvy 10.14savvy NSArray NSColor NSDictionary NSString NSUnarchiver | Leave a comment

Handle diff v2

Posted on 7月 3, 2019 by Takaaki Naganoya

Script Debugger上でオープン中の2つのAppleScript書類のうち、名称が同じハンドラ(サブルーチン)の内容同士を比較して、お互いに合っているかどうかをチェックするAppleScriptです。

アプリケーション書き出しして、Script Debuggerのスクリプトメニューに入れて実行します(AppleScript書類のままだと、Myriad Tables Lib内のフレームワークが呼べないため)。


▲本Scriptはスクリプトエディタの環境設定で行うAppleScriptの構文色分け設定色が「RGBカラー」でないとエラーになります。CMYKとかグレースケールカラーで指定して、そのままRGBに変換されずに保持される(CMYK色はアクセスしているうちにRGBに変換されるが、グレースケールは保持される)ため、色空間の判定を行ってRGB色に変換してから処理する必要があります(自分用メモ)

スクリプトエディタ上で設定する構文色分け設定をもとに、AppleScriptの構文要素を考慮したハンドラ一覧を作成し、2つのAppleScript書類の間に共通して存在するハンドラ名のハンドラ(サブルーチン)のAppleScriptソースを取得。インデント(tab)を削除したのちに比較を実行。

共通名称のハンドラすべてをチェックして、結果を表インタフェースで表示します。初版(v1)では実行するたびに1つのハンドラをチェックしていたのですが、あまりにかったるいので即座にループですべて調べてまとめて結果を表示するようにしました(v2)。

–> download executable archive (mainly for macOS 10.14 or later)

ただし、1つのAppleScript書類内にScript Object(JavaScriptでいうところのClosureみたいなもの)で論理分割された同名のハンドラが複数回登場するパターンは想定していません。複数検出するとエラーになります。

とくにScript Debuggerを対象にせずスクリプトエディタを対象にしてもよいのですが、些細な変更で実現できるため、興味のある方はやってみてください。あと、本Scriptは必要に迫られまくって作ったものなので、動作でおかしい箇所があったらぜひぜひご指摘を。

個人的には、ハンドラ名称を構文要素を考慮しつつ取得している一方で、ハンドラの内容は単なる文字列検索で取り出しているというあたりにアンバランスさを感じます。ただ、趣味のScriptではないので若干の手抜きを(^ー^;;

macOS 10.14.5上のスクリプトエディタで、構文色分け情報を何回設定しても「黒」になるという現象に遭遇したのですが、他のユーザー環境上での再現性については未知数であるため、まだレポートできない状況です。

→ その後、本ツールは強化されて、構文色分け設定にRGB以外の色が設定してあってもRGBに変換して処理する機能や、構文色分け設定を参照したコメント除去機能や、各種書式情報の取得を3倍高速化するカラーキャッシュなどの機能を実装したプログラムに(必要に迫られて)成長しました。

AppleScript名:Handle diff v2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/02
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5" — Sierra (10.12) or later (maybe 10.11 is OK but not confirmed)
use framework "Foundation"
use scripting additions
use framework "OSAKit"
use script "Myriad Tables Lib" version "1.0.9" –https://www.macosxautomation.com/applescript/apps/Script_Libs.html

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property OSAScript : a reference to current application’s OSAScript
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSUnarchiver : a reference to current application’s NSUnarchiver
property NSCountedSet : a reference to current application’s NSCountedSet

–構文色分け設定に重複色がないかチェック
set cList to getAppleScriptSourceColors() of me
set cRes to chkASLexicalFormatColorConfliction(cList) of me –構文色分けの重複色チェック
if cRes = false then error "There is some duplicate(s) color among AppleScript’s lexical color settings"

–Script Debugger上でオープン中の2つのAppleScript書類のパスを取得
set dList to retTwoPathList() of me
if dList = false then return

copy dList to {d1Path, d2Path}

–各AppleScript書類のハンドラ名称一覧を取得する
set hList1 to retHandlerNamesFromAppleScriptSource(d1Path as alias) of me
set hList2 to retHandlerNamesFromAppleScriptSource(d2Path as alias) of me

–2つのAppleScript書類のハンドラの共通項目(名称のみ)を抽出する
set resList to returnDuplicatesOnly(hList1 & hList2) of me
if resList = {} then
  –共通のハンドラ(サブルーチン)が指定の2つのScriptに存在していなかった
  
display dialog "There is no common handler between the scripts." buttons {"OK"} default button 1
  
return
end if

–各AppleScript書類のソースを取得して、指定のハンドラのテキストを取得する
set s1Source to getASsourceFor(d1Path as alias) of me
set s2Source to getASsourceFor(d2Path as alias) of me

if (s1Source = false) or (s2Source = false) then
  display dialog "Error occured in getteing AS source" buttons {"OK"} default button 1
  
return
end if

set outList to {}
repeat with i in resList
  set resHandler to contents of i
  
set s1Source to extractHandlerSourceOnly(s1Source, resHandler) of me
  
set s2Source to extractHandlerSourceOnly(s2Source, resHandler) of me
  
set compareF to (s1Source is equal to s2Source) as boolean
  
set the end of outList to {resHandler, compareF}
end repeat

–結果表示
tell me to activate
display table with data outList with prompt "Handler contents compare results" column headings {"Handler name", "Match?"} with title "Handler Diff Results"

–指定のハンドラの内容を抽出する(コメントぐらいは結果から削除してもいいような気もする)
–Script Objectの使用は考慮していない。1つのAppleScript書類からScript Objectで論理分割された同名のハンドラが複数検出されるケースは想定していない
on extractHandlerSourceOnly(wholeScript as string, handlerName as string)
  –インデント文字(Tab)をすべて削除
  
set targScript to repChar(wholeScript, tab, "") of me
  
  
–"on"ではじまるハンドラと仮定して抽出
  
set handlerStr to extractStrFromTo(targScript, "on " & handlerName, "end " & handlerName) of me
  
if handlerStr = false then
    –"to"ではじまるハンドラと仮定して抽出
    
set handlerStr to extractStrFromTo(targScript, "to " & handlerName, "end " & handlerName) of me
    
if handlerStr = false then return false
  else
    return handlerStr
  end if
end extractHandlerSourceOnly

on retHandlerNamesFromAppleScriptSource(aFile)
  set aRec to getAttrRecFromASPath(aFile) of me
  
set cList to getAppleScriptSourceColors() of me
  
  
set targAttr to contents of item 7 of cList –ハンドラあるいは変数
  
set tmpCoStr to ((redValue of targAttr) as string) & " " & ((greenValue of targAttr) as string) & " " & ((blueValue of targAttr) as string)
  
  
set ontoColItem to contents of item 3 of cList –スクリプティング予約語(on/to)
  
set ontoCoStr to ((redValue of ontoColItem) as string) & " " & ((greenValue of ontoColItem) as string) & " " & ((blueValue of ontoColItem) as string)
  
  
  
–変数あるいはハンドラ名称をリストアップ(variables & handler)
  
set tmp1Array to NSArray’s arrayWithArray:aRec
  
set thePred0 to NSPredicate’s predicateWithFormat_("colorStr == %@", tmpCoStr)
  
set dArray to (tmp1Array’s filteredArrayUsingPredicate:thePred0) as list
  
  
–改行を含むデータをリストアップ(text data contains return)
  
set thePred1 to NSPredicate’s predicateWithFormat_("stringVal CONTAINS %@", return)
  
set eArray to ((tmp1Array’s filteredArrayUsingPredicate:thePred1)’s valueForKeyPath:"itemIndex") as list
  
  
set the beginning of eArray to 0 –ハンドラ宣言部がTopに来る場合に備える
  
  
–"on"(ハンドラ宣言)の項目をリストアップ 文字と色で抽出
  
set thePred2 to NSPredicate’s predicateWithFormat_("stringVal == %@ && colorStr == %@ ", "on", ontoCoStr)
  
set fArray to ((tmp1Array’s filteredArrayUsingPredicate:thePred2)’s valueForKeyPath:"itemIndex") as list
  
  
–"to"(ハンドラ宣言ないしは代入対象指定) の項目をリストアップ文字と色で抽出
  
set thePred3 to NSPredicate’s predicateWithFormat_("stringVal == %@ && colorStr == %@ ", "to", ontoCoStr)
  
set gArray to ((tmp1Array’s filteredArrayUsingPredicate:thePred3)’s valueForKeyPath:"itemIndex") as list
  
  
  
set handlerList to {}
  
  
–on ではじまるハンドラの抽出
  
repeat with i in eArray –改行を含むテキストのアイテム番号リスト
    set j to (contents of i) as integer
    
repeat with ii in fArray –"on"の項目リスト
      set jj to (contents of ii) as integer
      
      
set handlerStr to missing value
      
      
if (j + 1) = jj then
        set handlerStr to stringVal of (item (jj + 2) of (aRec as list))
      else if (j + 2) = jj then
        set handlerStr to stringVal of (item (jj + 2) of (aRec as list))
      end if
      
      
if handlerStr is not in {"error", missing value} and handlerStr is not in handlerList then
        set the end of handlerList to handlerStr
      end if
      
    end repeat
  end repeat
  
  
  
–to ではじまるハンドラの抽出
  
repeat with i in eArray –改行を含むテキストのアイテム番号リスト
    set j to (contents of i) as integer
    
repeat with ii in gArray –"to"の項目リスト
      set jj to (contents of ii) as integer
      
      
set handlerStr to missing value
      
      
if (j + 1) = jj then
        set handlerStr to stringVal of (item (jj + 2) of (aRec as list))
      else if (j + 2) = jj then
        set handlerStr to stringVal of (item (jj + 2) of (aRec as list))
      end if
      
      
if handlerStr is not in {"error", missing value} and handlerStr is not in handlerList then
        set the end of handlerList to handlerStr
      end if
      
    end repeat
  end repeat
  
  
return handlerList
end retHandlerNamesFromAppleScriptSource

–ソースを取得してコンパイル(構文確認)する方式から、URL(fileURL)を指定してコンパイル(構文確認)する方式に変更した
on getAttrRecFromASPath(aFile)
  set aURL to current application’s |NSURL|’s fileURLWithPath:(POSIX path of aFile)
  
set theScript to OSAScript’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
  
  
if theScript is equal to missing value then
    — handle error
    
error "Compile Error"
  else
    –set sourceText to theScript’s source() –No Use
    
set styledSourceText to theScript’s richTextSource()
  end if
  
  
set attrRes to getAttributeRunsFromAttrString(styledSourceText) of me
  
return attrRes
end getAttrRecFromASPath

–指定AppleScriptファイルのソースコードを取得する(実行専用Scriptからは取得できない)
— Original Created 2014-02-23 Shane Stanley
on getASsourceFor(anAlias as {alias, string})
  set aURL to current application’s |NSURL|’s fileURLWithPath:(POSIX path of anAlias)
  
set theScript to OSAScript’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
  
  
if theScript is equal to missing value then
    — handle error
    
error "Compile Error"
  else
    set sourceText to theScript’s source()
  end if
  
  
return sourceText as string
end getASsourceFor

–Attributed StringをDictionary化
on getAttributeRunsFromAttrString(theStyledText)
  script aSpd
    property styleList : {}
  end script
  
  
set (styleList of aSpd) to {} —for output
  
  
set thePureString to theStyledText’s |string|() –pure string from theStyledText
  
  
set theLength to theStyledText’s |length|()
  
set startIndex to 0
  
set itemCount to 1
  
  
repeat until (startIndex = theLength)
    set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(specifier) inRange:{startIndex, theLength – startIndex}
    
    
–String  
    
set aText to (thePureString’s substringWithRange:theRange) as string
    
    
–Color
    
set aColor to (theAtts’s valueForKeyPath:"NSColor")
    
if aColor is not equal to missing value then
      set aSpace to aColor’s colorSpace()
      
      
set aRed to (aColor’s redComponent()) * 255
      
set aGreen to (aColor’s greenComponent()) * 255
      
set aBlue to (aColor’s blueComponent()) * 255
      
      
set colList to {aRed as integer, aGreen as integer, aBlue as integer} –for comparison
      
set colStrForFind to (aRed as integer as string) & " " & (aGreen as integer as string) & " " & (aBlue as integer as string) –for filtering
    else
      set colList to {0, 0, 0}
      
set colStrForFind to "0 0 0"
    end if
    
    
–Font
    
set aFont to (theAtts’s valueForKeyPath:"NSFont")
    
if aFont is not equal to missing value then
      set aDFontName to aFont’s displayName()
      
set aDFontSize to aFont’s pointSize()
    end if
    
    
set the end of (styleList of aSpd) to {stringVal:aText, colorStr:colStrForFind, colorVal:colList, fontName:aDFontName as string, fontSize:aDFontSize, itemIndex:itemCount}
    
set startIndex to current application’s NSMaxRange(theRange)
    
    
set itemCount to itemCount + 1
  end repeat
  
  
return (styleList of aSpd)
  
end getAttributeRunsFromAttrString

–AppleScriptの構文色分けのカラー値をRGBで取得する
on getAppleScriptSourceColors()
  
  
— get the plist info as a dictionary
  
set thePath to NSString’s stringWithString:"~/Library/Preferences/com.apple.applescript.plist"
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theInfo to NSDictionary’s dictionaryWithContentsOfFile:thePath
  
  
— extract relevant part and loop through
  
set theArray to (theInfo’s valueForKey:"AppleScriptSourceAttributes") as list
  
  
set colList to {}
  
  
repeat with i from 1 to count of theArray
    set anEntry to item i of theArray
    
    
set colorData to NSColor of anEntry
    
set theColor to (NSUnarchiver’s unarchiveObjectWithData:colorData)
    
    
set {rVal, gVal, bVal} to retColListFromNSColor(theColor, 255) of me
    
    
set fontData to NSFont of anEntry
    
set theFont to (NSUnarchiver’s unarchiveObjectWithData:fontData)
    
    
set aFontName to theFont’s displayName() as text
    
set aFontSize to theFont’s pointSize()
    
    
set aColRec to {redValue:rVal, greenValue:gVal, blueValue:bVal, fontName:aFontName, fontSize:aFontSize}
    
    
set the end of colList to aColRec
  end repeat
  
  
return colList
end getAppleScriptSourceColors

–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

–AS書式で配色に重複がないかどうかチェック
on chkASLexicalFormatColorConfliction(aList)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set bList to (anArray’s valueForKeyPath:"redValue.stringValue") as list
  
set cList to (anArray’s valueForKeyPath:"greenValue.stringValue") as list
  
set dList to (anArray’s valueForKeyPath:"blueValue.stringValue") as list
  
  
set colStrList to {}
  
repeat with i from 1 to (length of bList)
    set bItem to contents of item i of bList
    
set cItem to contents of item i of cList
    
set dItem to contents of item i of dList
    
set the end of colStrList to bItem & " " & cItem & " " & dItem
  end repeat
  
  
set aRes to returnDuplicatesOnly(colStrList) of me
  
log aRes
  
if aRes is equal to {} then
    return true –重複が存在しなかった場合
  else
    return false –重複があった場合
  end if
end chkASLexicalFormatColorConfliction

on returnDuplicatesOnly(aList as list)
  set aSet to current application’s 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

on retTwoPathList()
  tell application "Script Debugger"
    set dList to name of every document
    
set dResList to choose from list dList with prompt "Select two documents" with multiple selections allowed
    
if dResList = false then return false
    
if length of dResList is not equal to 2 then
      display dialog "Please Select two documents to compare handler contents" buttons {"OK"} default button 1 with icon 1 with title "Selection Error"
      
return false
    end if
    
    
set dPathList to {}
    
repeat with i in dResList
      set j to contents of i
      
tell document j
        set tmpPath to (file spec) as string –HFS path string
      end tell
      
      
set the end of dPathList to tmpPath
    end repeat
  end tell
  
  
return dPathList
end retTwoPathList

–Written By Philip Aker
–文字置換ルーチン
on repChar(origText as string, targStr as string, repStr as string)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

–指定文字と終了文字に囲まれた内容を抽出
on extractStrFromTo(aParamStr, fromStr, toStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set anArray to current application’s NSMutableArray’s array()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {theResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(specifier)
    
theScanner’s scanString:fromStr intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:toStr intoString:(specifier)
    
if theValue is missing value then set theValue to "" –>追加
    
theScanner’s scanString:toStr intoString:(missing value)
    
anArray’s addObject:theValue
  end repeat
  
  
if anArray’s |count|() is not equal to 1 then return false
  
  
return first item of (anArray as list)
end extractStrFromTo

★Click Here to Open This Script 

Posted in Color OSA Record Text | Tagged 10.12savvy 10.13savvy 10.14savvy NSArray NSCountedSet NSDictionary NSPredicate NSString NSUnarchiver OSAScript Script Debugger | Leave a comment

テキストから指定桁数の数値を抽出して度数分布集計

Posted on 6月 28, 2019 by Takaaki Naganoya

CotEditorで編集中の最前面の書類の本文中から指定桁の数値を抽出して登場回数で度数分布の集計を行うAppleScriptです。

CotEditorのアプリケーション内のスクリプトメニューに入れて実行できることを確認してあります(@macOS 10.14.5)。

たまたま、テキストで集計していた情報のうち、数値部分の度数分布を計算したいと考え、ありもののルーチンを組み合わせて作ったものです。

実行すると、集計対象の数値の桁数を聞いてくるため、適当なものを選択してださい。

CotEditorのScriptingの要注意点で、オブジェクト階層を素直に書いて、当該階層のオブジェクトの属性値を取りたいような場合に、そのまま属性を書いても取得できないケースが見られます。そのため、明示的にオブジェクト階層を指定するために「of it」を書いています。Xcodeが同じような挙動を行うため、注意が必要です。

本Scriptについていえば、正規表現でテキストから情報を抽出したりと、一応最低限のレベルはクリアしているものの、いまひとつ満足できません。

数字が連続して登場している箇所を抽出し、どれを集計対象にするのかといった分析を冒頭で行ってユーザーに問い合わせしたいところです。現段階では、「桁数を聞く」という頭の悪い実装になっていますが、そのうちどうにかしたいと考えるところです。

AppleScript名:テキストから指定桁数の数値を抽出して度数分布集計.scptd
— Created 2019-06-28 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

property graphStr : "絆" –グラフに出力する文字

tell application "CotEditor"
  –最前面の書類からテキスト本文を取得
  
tell front document
    set aText to contents of it –本文
    
set lineTerm to (line ending) –改行コード
  end tell
  
  
–改行コードの選択(ドキュメントの状態から取得)
  
if lineTerm = LF then
    set aRet to ASCII character 10 –To avoid keyword conflict (string)
  else if lineTerm = CR then
    set aRet to ASCII character 13
  else if lineTerm = CRLF then
    set aRet to (ASCII character 13) & (ASCII character 10)
  else
    set aRet to ASCII character 10
  end if
end tell

–桁数選択
set digiList to {"2", "3", "4", "5"}
set digitRes to first item of (choose from list digiList with prompt "Choose the digit to extract as numbers")

–正規表現で数字部分のみ抽出
set theList to my findPattern:("[0-9]{" & digitRes & "}") inString:aText

–検出した数字部分の度数分布を計算
set theCountedSet to current application’s NSCountedSet’s alloc()’s initWithArray:theList
set newArray to current application’s NSMutableArray’s new()
set kList to uniquifyAndSort1DList(theList, false) of me –降順ソート

repeat with i in kList
  (newArray’s addObject:{theKey:i, theCount:(theCountedSet’s countForObject:i)})
end repeat

–出力用のテキストを作成
set outStr to ""
repeat with i in newArray as list
  set outStr to (outStr & (theKey of i) as string) & ":"
  
set aNum to (theCount of i) as integer
  
  
repeat aNum times
    set outStr to outStr & graphStr
  end repeat
  
  
set outStr to outStr & aRet
end repeat

–最前面のテキスト末尾に集計結果を出力
tell application "CotEditor"
  tell front document
    set contents of it to (aText & aRet & aRet & "集計結果:" & aRet & aRet & outStr & aRet)
  end tell
end tell

–正規表現でテキスト中から指定パターンに該当する箇所を抽出してリストで返す
on findPattern:thePattern inString:theString
  set theOptions to ((current application’s NSRegularExpressionDotMatchesLineSeparators) as integer) + ((current application’s NSRegularExpressionAnchorsMatchLines) as integer)
  
set theRegEx to current application’s NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
  
set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString}
  
set theFinds to theFinds as list — so we can loop through
  
set theResult to {} — we will add to this
  
set theNSString to current application’s NSString’s stringWithString:theString
  
repeat with i from 1 to count of items of theFinds
    set theRange to (item i of theFinds)’s range()
    
set end of theResult to (theNSString’s substringWithRange:theRange) as integer
  end repeat
  
return theResult
end findPattern:inString:

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

★Click Here to Open This Script 

Posted in list Record regexp Sort Text | Tagged 10.12savvy 10.13savvy 10.14savvy CotEditor NSArray NSCountedSet NSMutableArray NSRegularExpression NSRegularExpressionDotMatchesLineSeparators NSSortDescriptor NSString | Leave a comment

レコードのリストを複数Keyでソート

Posted on 4月 19, 2019 by Takaaki Naganoya

リストに入れたレコードを複数キー、キーごとのソート方向(ascending/descending)指定でソートするAppleScriptです。

ソート対象のラベルはサンプルのように階層構造ごと指定できます。キーとして列挙するラベルは、最初のものが主キーで、2番目以降はサブキーです。

AppleScript名:レコードのリストを複数Keyでソート.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/04/19
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

set aRecList to {{aName:"piyoko", aVal:10, bVal:{x:3, y:1}}, {aName:"piyomaru", aVal:20, bVal:{x:5, y:2}}, {aName:"piyoo", aVal:20, bVal:{x:4, y:5}}, {aName:"piyozo", aVal:10, bVal:{x:10, y:9}}}

set bList to sortRecListByLabel(aRecList, {"aVal", "bVal.x"}, {true, false}) of me
–> {{aName:"piyozo", aVal:10, bVal:{x:10, y:9}}, {aName:"piyoko", aVal:10, bVal:{x:3, y:1}}, {aName:"piyomaru", aVal:20, bVal:{x:5, y:2}}, {aName:"piyoo", aVal:20, bVal:{x:4, y:5}}}

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList as list, aLabelStr as list, ascendF as list)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
  
set aCount to length of aLabelStr
  
set sortDescArray to current application’s NSMutableArray’s new()
  
repeat with i from 1 to aCount
    set aLabel to (item i of aLabelStr)
    
set aKey to (item i of ascendF)
    
set sortDesc to (current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabel ascending:aKey)
    (
sortDescArray’s addObject:sortDesc)
  end repeat
  
  
return (aArray’s sortedArrayUsingDescriptors:sortDescArray) as list
end sortRecListByLabel

★Click Here to Open This Script 

Posted in list Record Sort | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSMutableArray NSSortDescriptor | Leave a comment

指定フォルダ内のファイルのうち、指定UTIに該当するものをすべて取得してフルパスを返す

Posted on 4月 13, 2019 by Takaaki Naganoya

指定フォルダ内のファイルのうち、指定のUTIに該当するものをすべて取得してフルパス(file/POSIX)で返すAppleScriptです。

指定フォルダの直下のみを走査し、サブフォルダ内は走査しません。調べてみるとUTIでファイルを絞り込む機能は存在していないようなので(見落としているだけ?)、すべてファイルを取得してからループでUTIを調べつつ該当するかどうかチェックしています。

書けば書くほど「それってSpotlightでよくね?」という気がしますが、確実に取得したい(Spotlightインデックスが破損していたり、サーバー上のファイルではSpotlightが効かない場合もある)場合に使うとよいでしょうか。

AppleScript名:指定フォルダ内のファイルのうち、指定UTIに該当するものをすべて取得してフルパスを返す
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/04/13
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSPredicate : a reference to current application’s NSPredicate
property NSFileManager : a reference to current application’s NSFileManager
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles

set aUTI to "com.adobe.pdf"
set libPath to ((path to documents folder) as string)
set posixLibPath to POSIX path of libPath

set f1List to getFilepathListByUTI(posixLibPath, aUTI, "file") of me
–> {file "Cherry:Users:me:Documents:0718kenpo.pdf", file "Cherry:Users:me:Documents:2013-09Rekihaku.pdf", file "Cherry:Users:maro:Documents:airserver.pdf"}

set f2List to getFilepathListByUTI(posixLibPath, aUTI, "POSIX") of me
–> {"/Users/me/Documents/0718kenpo.pdf", "/Users/me/Documents/2013-09Rekihaku.pdf", "/Users/me/Documents/airserver.pdf"}

on getFilepathListByUTI(aFolPOSIX, aUTI as string, aFileType as string)
  script spdFile
    property urlList : {}
  end script
  
  
if aFileType is not in {"file", "POSIX"} then return {}
  
  
set aFM to NSFileManager’s defaultManager()
  
set aFolExt to (aFM’s fileExistsAtPath:aFolPOSIX isDirectory:true) as boolean
  
if aFolExt = false then return {} –フォルダ自体が存在しなければヌルリストを返す
  
  
set aURL to |NSURL|’s fileURLWithPath:aFolPOSIX
  
set theOptions to ((current application’s NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((current application’s NSDirectoryEnumerationSkipsHiddenFiles) as integer)
  
set urlArray to (aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value))
  
if urlArray = missing value then return {}
  
  
set (urlList of spdFile) to urlArray as list
  
set newList to {}
  
  
repeat with i in (urlList of spdFile)
    set j to POSIX path of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, aUTI)
    
    
if utiRes is not equal to {} then
      if aFileType = "POSIX" then
        set the end of newList to j
      else if aFileType = "file" then
        set the end of newList to POSIX file j
      end if
    end if
    
  end repeat
  
  
return newList
end getFilepathListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

★Click Here to Open This Script 

Posted in file File path list UTI | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSDirectoryEnumerationSkipsHiddenFiles NSFileManager NSPredicate NSURL NSURLTypeIdentifierKey | 3 Comments

指定単語の花文字テキストを取得する(Retina対応)英文字用 v2

Posted on 3月 29, 2019 by Takaaki Naganoya

英語版の花文字テキストを作成してテキストエディタ(CotEditor)に出力するAppleScriptです。

描画用の英単語と、塗りつぶし用の英単語を別々に指定できます。invertFをtrueに設定すると、白黒反転出力を行います。

おおもとの元ネタは、大型計算機を大勢のユーザーで共有していた時代(ぱそこん登場前)、計算結果を高速プリンタの帳票に出力するようになっていたころにさかのぼります。どのユーザーの計算結果かを大量のプリンタの出力結果の中から仕分けるため、印刷出力の1ページ目をユーザー名などの情報を印字した「バナー」として使用。

そのさい、ドットインパクトプリンターだったので、決められた固定ドット数の文字しか印字できないため、英数字を組み合わせて擬似的に大きな文字でバナーを印刷していたことに端を発します。

その時代の「残り香」ともいえるバナーがパソコンの電子メールやBBSの時代にお遊びの「花文字」として引き継がれ、原始的なアスキーアートの一種として楽しまれてきました。

いまでも、shell commandの「banner」コマンドにその末裔を見ることができます。

先週のmacOS nativeのデモ時に「祝」という花文字を「呪」の文字で出力するという、定番の持ちネタを披露したところ、たいへんウケておりました。

ただ、これを海外のScripter向けにそのままデモしても受けません。

この、花文字テキストを出力するプログラムの英語版としては、love.scptといういにしえのデータをそのまま使いまわしたものがありますが、描画パターンを数値データで保持しているため、異なるパターンを表示させることはできません。

そこで、日本語版の花文字テキスト出力プログラムに若干手をくわえ、英単語の出力が行えるようにしてみました。

テキストの出力はCotEditorに対して行なっていますが、新規書類を指定文字で作成する程度なので、AppleScriptに対応しているテキストエディタならどれでも作り変えられます。また、表示を等幅フォントにしておく必要があります(あとから指定してもOK)。

AppleScript名:指定単語の花文字テキストを取得する(Retina対応)英文字用 v2
— Created 2017-12-12 by Takaaki Naganoya
— Modified 2019-03-29 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

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

property fillCharList : {}
property fillCharCounter : 1

set aString to "Love"
set bString to "hate"
set hanaSize to 24
set thisFont to selectAFont(first character of aString) of me
if thisFont = false then return –Cancel

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

–花文字文字列を計算して返す
on getEnglishHanamojiStr(aFontSize as real, aFontName as string, aString as string, fillString as string, aThread as real, incFontName as boolean, invertF as boolean)
  
  
set my fillCharList to characters of fillString
  
set my fillCharCounter to 1
  
  
–指定文字コードが指定フォント中に存在するかチェック
  
repeat with i in (characters of aString)
    set fRes to retGlyphsInFont(aFontName, id of i) of me
    
if fRes = false then return false
  end repeat
  
  
set aThreadShould to 768 * aThread
  
if (chkMultiByteChar(first character of 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 invertF = false then
        
        
–通常描画(invert = false)時
        
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 getChar() of me
            else
              set the end of strListX to spaceChar
            end if
          else
            set the end of strListX to spaceChar
          end if
        end if
        
      else
        –白黒反転(invert = true)時
        
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 spaceChar
            else
              set the end of strListX to getChar() of me
            end if
          else
            set the end of strListX to getChar() of me
          end if
        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 "Font=" & fName & return & return & aRes
  end if
  
  
return aRes
end getEnglishHanamojiStr

on getChar()
  set retChar to item (my fillCharCounter) of (my fillCharList)
  
if (my fillCharCounter) = length of (my fillCharList) then
    set (my fillCharCounter) to 1
  else
    set (my fillCharCounter) to (my fillCharCounter) + 1
  end if
  
return retChar
end getChar

–指定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 

Posted in Color Image RTF Text | Tagged 10.12savvy 10.13savvy 10.14savvy CotEditor NSArray NSBezierPath NSBitmapImageRep NSColor NSColorSpace NSDictionary NSFont NSFontManager NSImage NSMutableAttributedString NSMutableDictionary NSPredicate NSString | 1 Comment

選択中のメッセージに添付ファイルがあれば赤く塗る

Posted on 3月 26, 2019 by Takaaki Naganoya

Mail.app上で選択中のメッセージ(Mail)に添付ファイルがあれば名称リストで背景色を赤く塗るAppleScriptです。

なんでこんなもんが必要なのか?

macOS 10.9あたりからでしょうか、Mail.appでGmailのアカウントのメールを取得するのに時間がかかったり、障害が発生するようになり、「とてもGmailを使っていられない」と感じました。

その結果、Gmailにこだわる必要もなかったので、Gmailアカウントは使わないようにしていました。macOS+Gmailだとトラブルの予感しかしません。

久しぶりにGmailのアカウントを使う必要があり、仕方なく使っていたのですが………やっぱりトラブルに遭遇。

ファイルが添付されたメールでも、受信メッセージ一覧に添付ファイルの表示が一切出ません(macOS 10.12.6+Mail 10.3)。気づいたときには久しぶりに震えましたが、再発防止のためには何かプログラム的なチェックを行う必要があります。

そこで、冒頭で説明したように「Mail.app上で選択中のメールに添付ファイルが存在しているものは、背景色を赤くする」というAppleScriptを作成した次第です。


▲Mail.appのメッセージ一覧(Classicレイアウト)で、本Scriptを用いて添付ファイルチェックを行なったところ。添付メッセージを検出して濃い赤で塗られているメッセージでも、Mail.appの添付ファイルインジケータに添付ファイルがあるように表示されない。Gmailアカウント以外でこのような症状に遭遇した経験はありません

とりあえず、「選択中のメッセージ」を処理するように書いてテストしましたが、Mail.appのメール受信時に実行するメールアクションで実行することが理想です。

ただ、macOS 10.13上ではMail関連(とくにメールアクション)がまともに動いていないので、「見なかったこと」にして、macOS 10.14上ではまともになっていることを祈るばかりです。そういう、macOS 10.13のような壊れ環境でも実行できるようにこのような(メールアクションではない形式の)Scriptで書いてみたという次第です。

AppleScript名:選択中のメッセージに添付ファイルがあれば赤く塗る.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/26
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

tell application "Mail"
  set aSel to selection
  
if aSel = {} then return
  
  
repeat with i in aSel
    set j to contents of i
    
    
set attList to mail attachment of j
    
    
set attNames to {}
    
repeat with ii in attList
      set jj to contents of ii
      
set aName to name of jj
      
set the end of attNames to aName
    end repeat
    
    
if attList is not equal to {} then
      set aLen to (length of attList)
      
display notification "Attached " & (aLen as string) & retMultiple(aLen) of me & "….." & retStrFromArrayWithDelimiter(attNames, ", ") of me
      
      
set background color of j to red
    end if
    
  end repeat
  
end tell

on retMultiple(aLen)
  if aLen = 0 then
    return " no item"
  else if aLen = 1 then
    return " item"
  else
    return " items"
  end if
end retMultiple

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as list of string or string
end retStrFromArrayWithDelimiter

★Click Here to Open This Script 

Posted in list | Tagged 10.12savvy Mail NSArray | Leave a comment

アラートダイアログ上に縦棒グラフを表示

Posted on 3月 4, 2019 by Takaaki Naganoya

アラートダイアログ上に縦棒グラフを表示するAppleScriptです。

グラフを作成するのであれば、NumbersやKeynoteなどを使ったほうがいいですが、簡単にデータの傾向だけダイアログ上で見せたいような場合のために作成した部品です。

NSImageViewの表示実験がしたかっただけなので、別に他の言語処理系で作成したグラフ画像を読み込んでNSImageに読み込んでからNSIMageViewで表示するぐらいの使い方でもぜんぜんOKでしょう。

WebViewを表示してインタラクティブなグラフ(マウスカーソルに反応してアニメーション表示を行うとか)を表示するといった使い方もよさそうです(アプリケーション化しないとダメかも)。

macOS 10.14上のDark Modeについては、実行環境によって対応度が異なります。


▲macOS 10.14上のDark Modeで実行したところ。左側からスクリプトエディタ、AppleScriptアプレット、スクリプトメニュー


▲macOS 10.14上のDark Modeで実行したところ。左側からScript Debugger、Script DebuggerからSD拡張アプレットで書き出したもの

グラフの描画については、色指定をblackColor()とかgrayColor()などと指定せずに、Dark Mode時に制御されるUI色で指定しておく必要があることでしょう。

AppleScript名:アラートダイアログ上に縦棒グラフを表示.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/04
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property NSFont : a reference to current application’s NSFont
property NSAlert : a reference to current application’s NSAlert
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 NSScreen : a reference to current application’s NSScreen
property NSDictionary : a reference to current application’s NSDictionary
property NSBezierPath : a reference to current application’s NSBezierPath
property NSImageView : a reference to current application’s NSImageView
property NSMutableArray : a reference to current application’s NSMutableArray
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName
property NSImageScaleProportionallyUpOrDown : a reference to current application’s NSImageScaleProportionallyUpOrDown

property returnCode : 0

set plotData to {20, 30, 100, 80, 150, 90}
set paramObj to {myMessage:"This is a graph", mySubMessage:"Browse bar graph", aPlotData:plotData}
my performSelectorOnMainThread:"displayBarGraph:" withObject:(paramObj) waitUntilDone:true

on displayBarGraph:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aTList to (aPlotData of paramObj) as list
  
  
— make NSIMageView with graph image
  
set anImage to makeGraphImage(aTList, 300, 200) of me
  
set anImageView to NSImageView’s alloc()’s initWithFrame:{origin:{x:0.0, y:0.0}, |size|:{width:300.0, height:200.0}}
  
anImageView’s setImageScaling:(NSImageScaleProportionallyUpOrDown)
  
anImageView’s setEditable:false
  
anImageView’s setImage:anImage
  
anImageView’s display()
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
its setAccessoryView:anImageView
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
end displayBarGraph:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

on makeGraphImage(plotData, aWidth, aHeight)
  set innerGapL to 30
  
set innerGapU to 10
  
set innerGapR to 20
  
set innerGapD to 20
  
set barGap to 10
  
  
–パラメータから下地になる画像を作成する
  
set aSize to current application’s NSMakeSize(aWidth, aHeight)
  
set anImage to NSImage’s alloc()’s initWithSize:aSize
  
  
–各種パラメータの計算
  
–copy plotArea to {plotWidth, plotHeight}
  
set itemNum to count every item of plotData
  
set barThickness to (aWidth – (itemNum * barGap * 2)) div itemNum
  
  
–プロットデータの最大値
  
set anArray to NSArray’s arrayWithArray:plotData
  
set aYmax to (anArray’s valueForKeyPath:"@max.self")’s intValue()
  
set aMaxYVal to aHeight – innerGapU – innerGapD
  
set aYPlotArea to aHeight – innerGapU – innerGapD – 20
  
set aYUnit to aYPlotArea / aYmax
  
  
–数値データをもとに描画データを組み立てる
  
set drawList to {}
  
  
set startX to innerGapL
  
copy startX to origX
  
  
repeat with i in plotData
    set the end of drawList to current application’s NSMakeRect(startX, innerGapD, barThickness, innerGapD + (i * aYUnit))
    
set startX to startX + barThickness + barGap
  end repeat
  
  
–グラフ塗りつぶし処理呼び出し
  
set fillColor to (NSColor’s colorWithCalibratedRed:0.1 green:0.1 blue:0.1 alpha:0.3)
  
set resImage to drawImageWithColorFill(anImage, drawList, fillColor) of me
  
  
–数値データ(文字)をグラフィックに記入
  
set fillColor2 to NSColor’s blackColor()
  
set resImage to drawImageWithString(resImage, drawList, fillColor2, plotData, "Eurostile Bold", 20.0) of me
  
  
–補助線を引く
  
set fillColor3 to (NSColor’s colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:0.8)
  
set aVertical to current application’s NSMakeRect(origX, innerGapD, aWidth – innerGapL – innerGapR, 1)
  
set aHorizontal to current application’s NSMakeRect(origX, innerGapD, 1, aHeight – innerGapU – innerGapD)
  
set draw2List to {aVertical, aHorizontal}
  
set resImage to drawImageWithColorFill(resImage, draw2List, fillColor3) of me
  
  
return resImage
end makeGraphImage

–NSImageに対して文字を描画する
on drawImageWithString(anImage, drawList, fillColor, dataList, aPSFontName, aFontSize)
  set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
  
–>  2.0 (Retina) / 1.0 (Non Retina)
  
  
set aDict to (NSDictionary’s dictionaryWithObjects:{NSFont’s fontWithName:aPSFontName |size|:aFontSize, fillColor} forKeys:{NSFontAttributeName, NSForegroundColorAttributeName})
  
  
anImage’s lockFocus() –描画開始
  
  
set aLen to length of drawList
  
repeat with i from 1 to aLen
    set i1 to contents of item i of drawList
    
    
set v2 to system attribute "sys2"
    
if v2 ≤ 12 then
      –To macOS 10.12.x
      
set origX to (x of origin of i1) / retinaF
      
set origY to (y of origin of i1) / retinaF
      
set sizeX to (width of |size| of i1) / retinaF
      
set sizeY to (height of |size| of i1) / retinaF
      
set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}}
    else
      –macOS 10.13 or later
      
set origX to (item 1 of item 1 of i1) / retinaF
      
set origY to (item 2 of item 1 of i1) / retinaF
      
set sizeX to (item 1 of item 2 of i1) / retinaF
      
set sizeY to (item 2 of item 2 of i1) / retinaF
      
set theRect to {{origX, origY}, {sizeX, sizeY}}
    end if
    
    
set aString to (current application’s NSString’s stringWithString:((contents of item i of dataList) as string))
    (
aString’s drawAtPoint:(current application’s NSMakePoint(origX + (sizeX / 2), sizeY)) withAttributes:aDict)
  end repeat
  
  
anImage’s unlockFocus() –描画ここまで
  
  
return anImage –returns NSImage
end drawImageWithString

–NSImageに対して矩形を塗りつぶす
on drawImageWithColorFill(anImage, drawList, fillColor)
  set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
  
–>  2.0 (Retina) / 1.0 (Non Retina)
  
  
anImage’s lockFocus() –描画開始
  
  
repeat with i in drawList
    set v2 to system attribute "sys2"
    
if v2 ≤ 12 then
      –To macOS 10.12.x
      
set origX to (x of origin of i) / retinaF
      
set origY to (y of origin of i) / retinaF
      
set sizeX to (width of |size| of i) / retinaF
      
set sizeY to (height of |size| of i) / retinaF
      
set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}}
    else
      –macOS 10.13 or later
      
set origX to (item 1 of item 1 of i) / retinaF
      
set origY to (item 2 of item 1 of i) / retinaF
      
set sizeX to (item 1 of item 2 of i) / retinaF
      
set sizeY to (item 2 of item 2 of i) / retinaF
      
set theRect to {{origX, origY}, {sizeX, sizeY}}
    end if
    
    
set theNSBezierPath to NSBezierPath’s bezierPath
    (
theNSBezierPath’s appendBezierPathWithRect:theRect)
    
    
fillColor’s |set|() –色設定
    
theNSBezierPath’s fill() –ぬりつぶし
  end repeat
  
  
anImage’s unlockFocus() –描画ここまで
  
  
return anImage –returns NSImage
end drawImageWithColorFill

★Click Here to Open This Script 

Posted in GUI Image list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAlert NSAlertSecondButtonReturn NSArray NSBezierPath NSBitmapImageRep NSColor NSDictionary NSFont NSFontAttributeName NSForegroundColorAttributeName NSImage NSImageScaleProportionallyUpOrDown NSImageView NSMutableArray NSRunningApplication NSScreen NSString | Leave a comment

「戦場の絆」の日本国内の設置店舗数をカウント

Posted on 3月 3, 2019 by Takaaki Naganoya

アーケードゲーム「戦場の絆」が設置されている日本国内のゲームセンターの件数をカウントするAppleScriptです。

指定ページを取得し、本文内容をプレーンテキスト化して正規表現でデータ抽出して加算を行うという処理内容です。

部品がありきたりなので、新規作成部分はほとんどありません(メイン部分だけ)。

AppleScript名:戦場の絆の日本国内の設置店舗数をカウント.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/03
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSPredicate : a reference to current application’s NSPredicate
property NSURLQueryItem : a reference to current application’s NSURLQueryItem
property NSAttributedString : a reference to current application’s NSAttributedString
property NSURLComponents : a reference to current application’s NSURLComponents
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSUnicodeStringEncoding : a reference to current application’s NSUnicodeStringEncoding

set aTotal to 0

–「戦場の絆」公式ページに掲載されている店舗一覧の都道府県別コード
set areaList to {"JP-01", "JP-02", "JP-03", "JP-04", "JP-05", "JP-06", "JP-07", "JP-08", "JP-09", "JP-10", "JP-11", "JP-12", "JP-13", "JP-14", "JP-15", "JP-16", "JP-17", "JP-18", "JP-19", "JP-20", "JP-21", "JP-22", "JP-23", "JP-24", "JP-25", "JP-26", "JP-27", "JP-28", "JP-29", "JP-30", "JP-31", "JP-32", "JP-33", "JP-34", "JP-35", "JP-36", "JP-37", "JP-38", "JP-39", "JP-40", "JP-41", "JP-42", "JP-43", "JP-44", "JP-45", "JP-46", "JP-47"}

–「戦場の絆」公式ページに掲載されている店舗一覧のBase URL
set baseURL to "https://gundam-kizuna.jp/locations/list"

–都道府県コードでループ
repeat with i in areaList
  –店舗一覧の都道府県別ページをダウンロード
  
set j to contents of i
  
set aRec to {area:j}
  
set aRes to retURLwithParams(baseURL, aRec) of me
  
set shellText to "curl -s " & aRes
  
set htmlRes to (do shell script shellText)
  
set aPlainText to (HTMLDecode(htmlRes) of me)
  
  
–店舗件数が入っている箇所を抽出して加算
  
set aList to paragraphs of aPlainText
  
set anArray to (NSArray’s arrayWithArray:aList)
  
set aPred to (NSPredicate’s predicateWithFormat:"SELF MATCHES ’[0-9]{1,3}件の店舗が見つかりました。’")
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
  
if bRes is not equal to {} then
    set a to repChar(contents of first item of bRes, "件の店舗が見つかりました。", "") of me
    
try
      set aNum to a as integer
      
set aTotal to aTotal + aNum
    on error
      log {"Error in ", j}
    end try
  end if
end repeat

return aTotal
–> 622 (2019/3/3)

(*
set countryList to {"HKG", "TWN"}–香港と台湾の店舗のコード
set baseURL2 to "https://gundam-kizuna.jp/locations/list"
*)

–パラメータつきURLを作成
on retURLwithParams(aBaseURL as string, aRec as record)
  set aDic to NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
set bLen to length of aValList
  
if aLen is not equal to bLen then return false
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to (contents of item i of aKeyList) as string
    
set aVal to (contents of item i of aValList) as string
    
set the end of qList to (NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

–テキストをHTMLとして解釈しプレーンテキスト化
on HTMLDecode(HTMLString)
  set theString to NSString’s stringWithString:HTMLString
  
set theData to theString’s dataUsingEncoding:(current application’s NSUnicodeStringEncoding)
  
set attStr to NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)
  
set aStr to attStr’s |string|()
  
return aStr as string
end HTMLDecode

–文字置換
on repChar(origText as string, targStr as string, repStr as string)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

★Click Here to Open This Script 

Posted in list regexp Text URL | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSAttributedString NSMutableDictionary NSPredicate NSString NSUnicodeStringEncoding NSURLComponents NSURLQueryItem | Leave a comment

指定ファイルサイズのJPEGに変換(圧縮率可変)_1K=1024で計算

Posted on 2月 11, 2019 by Takaaki Naganoya

Finder上で選択中の画像ファイルを指定ファイルサイズを下回るよう圧縮率を調整してJPEGに変換するAppleScriptです。

指定画像を指定のファイルサイズ(1K=1024で計算)以内になるよう、非圧縮の状態から段階的に圧縮率を高くして仕上がりファイルサイズを下回るかどうか演算を行います。ファイルに書き込まずに仕上がりサイズの計算を行うところが新機軸です。

macOS標準搭載のスクリプトメニューから呼び出して使うことを想定しています。実行すると、ダイアログ表示して指定ファイルサイズをKB単位で数値入力する必要があります。

巨大な画像ファイルを処理してしまった場合への対処として、最初に最高圧縮率で圧縮してみて、指定ファイルサイズ以内になるかどうかを計算します。

最初は圧縮率100%から25%まで1%きざみで仕上がり画像ファイルサイズを計算してみたのですが、数十Mバイトの巨大な画像を処理させたら、途中でマウスカーソルが反応しないほどOSが無反応になったので(暴走状態?)、それを避けるために、このような処理を行なってみました。

JPEGにファイル変換した画像はデスクトップに

UUID_JPEG画質.jpg

の形式で出力されます。JPEG画質は1.0から0.01までの数値です。

AppleScript名:指定ファイルサイズのJPEGに変換(圧縮率可変)_1K=1024で計算
— Created 2014-02-21 Shane Stanley
— Modified 2019-02-11 Takaaki Naganoya
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property |NSURL| : a reference to current application’s |NSURL|
property NSUUID : a reference to current application’s NSUUID
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 NSJPEGFileType : a reference to current application’s NSJPEGFileType
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey

property compressList : {100, 99, 98, 97, 96, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 40, 30, 20, 25} –大→小 へと並んでいる必要がある
property kbBase : 1024

tell application "Finder"
  set inFiles to selection as alias list
end tell

–指定のAlias listのうち画像ファイルのみ抽出
set filRes1 to filterAliasListByUTI(inFiles, "public.image") of me
if filRes1 = {} then return

–ターゲットサイズを指定
set aMessage to "Input Target size in KB ( " & (kbBase as string) & " based )"
set aRes to text returned of (display dialog aMessage default answer "300" buttons {"OK"} default button 1)
if aRes = false or aRes = "" then return
set targLimit to (aRes as number) * kbBase

–選択中のファイルのうちの1つから親フォルダを求め、出力先ファイルパスを組み立てる
set outPathTarg to (contents of first item of filRes1)
set pathString to NSString’s stringWithString:outPathTarg
set newPath to (pathString’s stringByDeletingLastPathComponent()) as string

–Main Loop
repeat with i in filRes1
  set aNSImage to (NSImage’s alloc()’s initWithContentsOfFile:(i))
  
  
–巨大すぎる画像のリサイズ時への保険で、最小サイズで投機的に圧縮して結果を確認する
  
set minSize to calcSavedJPEGSizeFromNSIMage(aNSImage, (last item of compressList) / 100) of me
  
  
if minSize < targLimit then
    –Simulate Suitable JPEG Compression Ratio to target file size
    
repeat with ii in compressList
      set jj to ii / 100
      
set fileSize to calcSavedJPEGSizeFromNSIMage(aNSImage, jj) of me
      
      
if fileSize < targLimit then
        exit repeat
      end if
    end repeat
  else
    set jj to 0.01 –エラー時にはやけくそで1%まで圧縮指定
  end if
  
  
set outPOSIXpath to (newPath & "/" & (NSUUID’s UUID()’s UUIDString()) as string) & "_" & (jj as string)
  
set savePath to outPOSIXpath & ".jpg"
  
  
saveNSImageAtPathAsJPG(aNSImage, savePath, jj) of me
end repeat

–Alias listから指定UTIに含まれるものをPOSIX pathのリストで返す
on filterAliasListByUTI(aList, targUTI)
  set newList to {}
  
repeat with i in aList
    set j to POSIX path of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, targUTI)
    
if utiRes is not equal to {} then
      set the end of newList to j
    end if
  end repeat
  
return newList
end filterAliasListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

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

–NSImageをJPEG形式で保存したときのファイルサイズを計算
on calcSavedJPEGSizeFromNSIMage(anImage, qulityNum as real)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:imageRep
  
set myNewImageData to (aRawimg’s representationUsingType:(NSJPEGFileType) |properties|:{NSImageCompressionFactor:qulityNum})
  
set aLength to (myNewImageData’s |length|()) as number
  
return aLength
end calcSavedJPEGSizeFromNSIMage

★Click Here to Open This Script 

Posted in file Image list UTI | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Finder NSArray NSBitmapImageRep NSImage NSPredicate NSString NSURL NSURLTypeIdentifierKey NSUUID | Leave a comment

Finder上で選択中のAppleScriptの行数をカウントする

Posted on 2月 8, 2019 by Takaaki Naganoya

Finder上で選択中のAppleScript書類の行数や種別を取得して表UIで表示するAppleScriptです。

Myriad Tables Libを用いて結果を表UIで表示しています。

当初はAppleScript書類からソースを取得するのに/usr/bin/osadecompileコマンドを使っていたのですが、OSAKit経由で処理したほうが高速な印象があります。

–> Download Executable archive for macOS 10.14 (include Myriad Tables Lib)

macOS 10.14上で動作させるためには、Script Debugger上で本Scriptをオープンするか、本Scriptをバンドル形式で保存し、バンドル内(/Contents/Resources/Script Libraries フォルダ)にMyriad Tables Libを入れ、AppleScriptアプレットとして書き出す必要があります。

また、初回実行時には「セキュリティ」の承認ダイアログが表示されるため、これをOKする必要があります。AppleScriptアプレットはFinder上でアイコンをダブルクリックすると「Finder上で選択中のアイテム」を拾えないため(その瞬間、Finder上での選択アイテムはアプレット自身になってしまうため)、DockやmacOS標準装備のスクリプトメニューに登録して起動する必要があります。

AppleScript名:Finder上で選択中のAppleScriptの行数をカウントする v2.scpt
— Created 2019-02-04 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "OSAKit"
use script "Myriad Tables Lib" version "1.0.8" –https://www.macosxautomation.com/applescript/apps/Script_Libs.html#MyriadTablesLib

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 OSAScript : a reference to current application’s OSAScript
property NSPredicate : a reference to current application’s NSPredicate
property NSURLIsPackageKey : a reference to current application’s NSURLIsPackageKey
property NSURLIsDirectoryKey : a reference to current application’s NSURLIsDirectoryKey
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey

–Finder上で選択中のファイルのうちAppleScript書類のみ抽出
tell application "Finder"
  set inFiles to selection as alias list
  
if inFiles = {} then return
end tell
set filRes1 to filterAliasListByUTIList(inFiles, {"com.apple.applescript.script", "com.apple.applescript.script-bundle"}) of me
if filRes1 = {} then return

–AppleScript書類の種別判定および行数カウント
set outList to {}
repeat with i in filRes1
  set aName to (NSString’s stringWithString:i)’s lastPathComponent() as string
  
set sInfo to detectScriptIsPureASorASOC(i) of me
  
set sText to scriptSource of getASsourceFor(i) of me
  
set sNum to count every paragraph of sText
  
set sUTI to retUTIfromPath(i) of me
  
if sUTI = "com.apple.applescript.script" then
    set sKind to "Script"
  else if sUTI = "com.apple.applescript.script-bundle" then
    set sKind to "Script Bundle"
  else
    set sKind to "Other"
  end if
  
  
set the end of outList to {aName, sInfo, sKind, sNum}
end repeat

–結果を表UIで表示する
tell script "Myriad Tables Lib"
  set aDispBounds to my makeInitialBounds:1200 withHeight:500
  
set theTable to make new table with data outList column headings {"script name", "ASOC", "Script Kind", "lines"} with title "AppleScript Line Count" with prompt "Your Selected Scripts" with row numbering and empty selection allowed –and can add and delete
  
modify table theTable initial position aDispBounds column widths pattern {1, 2, 3, 4} with hidden cancel button
  
modify columns in table theTable user date format {user format full, user format full} entry alignment align left
  (
display table theTable)
end tell

–Alias listから指定UTI Listに含まれるものをPOSIX pathのリストで返す
on filterAliasListByUTIList(aList as list, targUTIList as list)
  set outList to {}
  
repeat with i in targUTIList
    set j to contents of i
    
set aRes to filterAliasListByUTI(aList, j) of me
    
if aRes is not equal to {} then
      set outList to outList & aRes
    end if
  end repeat
  
return outList
end filterAliasListByUTIList

–Alias listから指定UTIに含まれるものをPOSIX pathのリストで返す
on filterAliasListByUTI(aList as list, targUTI)
  set newList to {}
  
repeat with i in aList
    set j to POSIX path of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, targUTI)
    
if utiRes is not equal to {} then
      set the end of newList to j
    end if
  end repeat
  
return newList
end filterAliasListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(specifier) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

on makeInitialBounds:(aTWidth as integer) withHeight:(aTHeight as integer)
  set aDispBounds to current application’s NSScreen’s mainScreen()’s frame()
  
if class of item 1 of aDispBounds = record then
    –macOS 10.10/10.11/10.12
    
set aWidth to (width of |size| of aDispBounds)
    
set aHeight to (height of |size| of aDispBounds)
  else
    –macOS 10.13 or later?
    
set aWidth to (item 1 of item 2 of aDispBounds)
    
set aHeight to (item 2 of item 2 of aDispBounds)
  end if
  
  
set xPos to (aWidth div 2) – (aTWidth div 2)
  
set yPos to (aHeight div 2) – (aTHeight div 2)
  
  
return {xPos, yPos, aTWidth, aTHeight}
end makeInitialBounds:withHeight:

–指定AppleScriptファイルがPure ASかASOCかを判定して返す
on detectScriptIsPureASorASOC(aFile)
  set sRes to getASsourceFor(aFile) of me
  
set sName to scriptKind of sRes –Name
  
set sText to scriptSource of sRes –Source
  
if sText = "" or sText = missing value then return missing value
  
  
if sName = "AppleScript" then
    if sText contains "use framework \"Foundation\"" then
      return true –ASOC
    else
      return false –Pure AppleScript
    end if
  else
    –JXAなど他のOSA言語の場合
    
return sName
  end if
end detectScriptIsPureASorASOC

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

★Click Here to Open This Script 

Posted in file File path GUI OSA | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSPredicate NSString NSURL OSAScript | Leave a comment

Keynote書類の現在のスライド上の表1の背景色を置換 v1

Posted on 1月 31, 2019 by Takaaki Naganoya

Keynoteでオープン中の最前面の書類の現在表示中のスライドに存在する表1の背景色を置換するAppleScriptです。

Pages用のScriptをごく一部修正してkeynoteの表に対して処理できるようにしてみました。macOS 10.11, 10.12, 10.13ではスクリプトエディタ上で動作します。macOS 10.14ではSIPを解除してスクリプトエディタで動かすか、アプレット形式で書き出して、アプレットのバンドル中にdbColNamesKit.frameworkを入れると動きます。

Script DebuggerとScript Menu上では動作しません。

ポップアップメニュー中に色の名称の動的な推定に、オープンソースの「DBColorNames」をフレームワーク化した
「dbColNamesKit.framework」を利用しています。

–> dbColNamesKit.framework (To ~/Library/Frameworks)

AppleScript名:Keynote書類の現在のスライド上の表の背景色を置換 v1
— Created 2019-01-29 by Takaaki Naganoya
— 2019 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/
use Bplus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

–v1:Convert Pages version to Keynote

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 NSFloatingWindowLevel : a reference to current application’s NSFloatingWindowLevel
property NSBackingStoreBuffered : a reference to current application’s NSBackingStoreBuffered
property NSMomentaryLightButton : a reference to current application’s NSMomentaryLightButton

property windisp : true
property wController : missing value
property pop1ind : 1

–初期化
set (my windisp) to true
set (my pop1ind) to 1
load framework

–Pagesの1ページ目にある表の塗り色を取得
tell application "Keynote"
  tell front document
    tell current slide
      tell table 1
        set c1List to background color of every cell
        
set aProp to properties
        
set xCount to column count of aProp
      end tell
    end tell
  end tell
end tell

–Convert 1D List to 2D List
set c3List to (current application’s SMSForder’s subarraysFrom:c1List groupedBy:xCount |error|:(missing value)) as list

–色データをユニーク化(重複削除)
set bList to uniquifyList(c1List) of me

–missing value(背景色なし)を除外する
set c2List to (current application’s SMSForder’s arrayByDeletingBlanksIn:(bList)) as list

–Popup Menuで置換色選択
set paramObj to {c2List, 65535, "OK", "Select Target Color", 180} –Timeout = 180 sec, Color val range = 16bit
my performSelectorOnMainThread:"getPopupValues:" withObject:(paramObj) waitUntilDone:true
if pop1ind = false then return –timed out
set fromCol to (contents of item pop1ind of c2List)

–カラーピッカーで置換色選択
set tCol to choose color default color fromCol

set d1 to current date

–実際に表の背景色を置換する
set hitList to findDataFrom2DList(fromCol, c3List) of me –データ上で当該色のセル情報を計算する

–Rangeを横スキャンと縦スキャンの2通りで試算(Two way Simulation)
set rList1 to retRangeFromPosListHorizontal(hitList) of me –横方向へのrange評価
set rList2 to retRangeFromPosListVertival(hitList) of me –縦方向へのrange評価

–Simulationの結果、要素数の少ない方(=処理時間の短い方=高速な方)を採用する
log {"Simulation", (length of rList1), (length of rList2)}
if (length of rList1) < (length of rList2) then
  copy rList1 to rangeList
else
  copy rList2 to rangeList
end if

tell application "Keynote"
  activate
  
tell front document
    tell current slide
      tell table 1
        repeat with i in rangeList
          set j to contents of i
          
          
ignoring application responses –非同期実行モードで高速実行
            set background color of range j to tCol
          end ignoring
          
        end repeat
      end tell
    end tell
  end tell
end tell

set d2 to current date
return d2 – d1

–カラーポップアップメニューをウィンドウ表示
on getPopupValues:paramObj
  copy (paramObj as list) to {ap1List, aColMax, aButtonMSG, aSliderValMSG, 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 aCDB to current application’s DBColorNames’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 makeRoundedNSImageWithFilledWithColor(64, 64, nsCol, 4) of me
    
    
set aTitle to "#" & (iCount as string) & " " & (aCDB’s nameForColor:nsCol) 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:(NSMomentaryLightButton)
  
bButton’s setBezelStyle:(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, aSliderValMSG))
  
  
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 number) + 1)
  else
    set s1Val to false
  end if
  
  
copy s1Val to my pop1ind
  
end getPopupValues:

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

–make Window for Display
on makeWinWithView(aView, aWinWidth as integer, aWinHeight as integer, aTitle as string)
  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:(NSFloatingWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
  
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:

–aMaxValを最大値とする数値でNSColorを作成して返す
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

–指定サイズのNSImageを作成し、指定色で塗ってNSImageで返す
on makeNSImageWithFilledWithColor(aWidth as integer, aHeight as integer, 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

–指定サイズのNSImageを作成し、指定色で塗ってNSImageで返す、anRadiusの半径の角丸で
on makeRoundedNSImageWithFilledWithColor(aWidth as integer, aHeight as integer, fillColor, anRadius as real)
  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 bezierPathWithRoundedRect:theRect xRadius:anRadius yRadius:anRadius
  
—
  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  
—
  
anImage’s unlockFocus()
  
  
return anImage
end makeRoundedNSImageWithFilledWithColor

on uniquifyList(aList as list)
  set aArray to NSArray’s arrayWithArray:aList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
return bArray as list
end uniquifyList

on findDataFrom2DList(anItem, aList as list)
  script spd
    property aList : {}
    
property resList : {}
  end script
  
  
set (aList of spd) to aList
  
set (resList of spd) to {}
  
  
set yCount to 1
  
  
repeat with i in (aList of spd)
    
    
set aResList to (Bplus’s indexesOfItem:anItem inList:i inverting:false) as list
    
    
set tmpList to {}
    
if aResList is not equal to {} then
      repeat with ii in aResList
        set jj to contents of ii
        
set the end of tmpList to {jj, yCount}
      end repeat
      
set (resList of spd) to (resList of spd) & tmpList
    end if
    
    
set yCount to yCount + 1
  end repeat
  
  
return (resList of spd) –return {{x, y}…..} item list (1-based)
end findDataFrom2DList

on retRangeFromPosListVertival(posList as list)
  script rangeSPD
    property posList2 : {}
  end script
  
  
–縦方向へのrange評価に都合がいいようにソート
  
set (posList2 of rangeSPD) to shellSortListAscending(posList, {1, 2}) of me
  
  
–先頭データをピックアップ
  
set firstData to first item of (posList2 of rangeSPD)
  
set (posList2 of rangeSPD) to rest of (posList2 of rangeSPD)
  
  
copy firstData to {curX1, curY1}
  
set tmpRangeStr to aNumToExcelColumn(curX1) of me & (curY1 as string) & ":"
  
  
set tmpRange to {}
  
set hitF to false
  
  
set outList to {}
  
  
repeat with i in (posList2 of rangeSPD)
    copy i to {tmpX, tmpY}
    
    
–log {"{curX1, curY1}", {curX1, curY1}}
    
–log {"{tmpX, tmpY}", {tmpX, tmpY}}
    
    
    
if (curX1 = tmpX) and (curY1 + 1 = tmpY) then
      –Y方向への連続値を拾っている最中
      
if hitF = false then
        –log "case 1a"
        
–log {"hitF", hitF}
        
set hitF to true
      else
        –log "case 1b"
        
–log {"hitF", hitF}
        
–横に連続しているブロックの途中
      end if
    else
      –直前の値と連続していない
      
if hitF = false then
        –log "case 2a"
        
–log {"hitF", hitF}
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
      else
        –log "case 2b"
        
–log {"hitF", hitF}
        
–連続ブロックの末尾を拾った
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
        
–log {"tmpRangeStr", tmpRangeStr}
      end if
    end if
    
    
copy {tmpX, tmpY} to {curX1, curY1}
  end repeat
  
  
–log {tmpRangeStr, hitF}
  
  
if (hitF = true) or (tmpRangeStr is not equal to "") then
    set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
    
set the end of outList to tmpRangeStr
  end if
  
  
return outList
end retRangeFromPosListVertival

on retRangeFromPosListHorizontal(posList as list)
  script rangeSPD
    property posList2 : {}
  end script
  
  
copy posList to (posList2 of rangeSPD)
  
  
–先頭データをピックアップ
  
set firstData to first item of (posList2 of rangeSPD)
  
set (posList2 of rangeSPD) to rest of (posList2 of rangeSPD)
  
  
copy firstData to {curX1, curY1}
  
set tmpRangeStr to aNumToExcelColumn(curX1) of me & (curY1 as string) & ":"
  
  
set tmpRange to {}
  
set hitF to false
  
  
set outList to {}
  
  
repeat with i in (posList2 of rangeSPD)
    copy i to {tmpX, tmpY}
    
    
–log {"{curX1, curY1}", {curX1, curY1}}
    
–log {"{tmpX, tmpY}", {tmpX, tmpY}}
    
    
    
if (curX1 + 1 = tmpX) and (curY1 = tmpY) then
      –X方向への連続値を拾っている最中
      
if hitF = false then
        –log "case 1a"
        
–log {"hitF", hitF}
        
set hitF to true
      else
        –log "case 1b"
        
–log {"hitF", hitF}
        
–横に連続しているブロックの途中
      end if
    else
      –直前の値と連続していない
      
if hitF = false then
        –log "case 2a"
        
–log {"hitF", hitF}
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
      else
        –log "case 2b"
        
–log {"hitF", hitF}
        
–連続ブロックの末尾を拾った
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
        
–log {"tmpRangeStr", tmpRangeStr}
      end if
    end if
    
    
copy {tmpX, tmpY} to {curX1, curY1}
  end repeat
  
  
–log {tmpRangeStr, hitF}
  
  
if (hitF = true) or (tmpRangeStr is not equal to "") then
    set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
    
set the end of outList to tmpRangeStr
  end if
  
  
return outList
end retRangeFromPosListHorizontal

–2008/05/01 By Takaaki Naganoya
–10進数数値をExcel 2004/2008的カラム表現にエンコードするサブルーチン を使いまわし
–1〜1351までの間であれば正しいエンコーディング結果を返す
on aNumToExcelColumn(origNum as integer)
  if origNum > 1351 then
    error "エラー:Excel 2004/2008的カラム表現(A1形式)への変換ルーチンにおいて、想定範囲外(1351以上)のパラメータが指定されました"
  end if
  
  
set upperDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
set lowerDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
  
set oNum to origNum
  
set nTh to 26
  
set stringLength to 4
  
  
–数字が1桁の場合の対応
  
if origNum < 27 then
    set aRes to (item origNum of upperDigitEncTable) as string
    
return aRes
  end if
  
  
  
if origNum > 702 then
    –3桁になる場合
    
set upupNum to oNum div 676 –整数除算–上の上の桁
    
set oNum to oNum – (upupNum * 676)
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–超つじつま合わせ処理
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upupChar to (item upupNum of upperDigitEncTable) as string
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upupChar & upChar & lowChar
    
  else
    –2桁の場合
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–超つじつま合わせ処理
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upChar & lowChar
    
  end if
  
  
return resText
end aNumToExcelColumn

–入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  return sort2DList(a, keyItem, {true}) of me
end shellSortListAscending

–入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  return sort2DList(a, keyItem, {false}) of me
end shellSortListDecending

–2D Listをソート
on sort2DList(aList as list, sortIndexes as list, sortOrders as list)
  
  
–index値をAS流(アイテムが1はじまり)からCocoa流(アイテムが0はじまり)に変換
  
set newIndex to {}
  
repeat with i in sortIndexes
    set j to contents of i
    
set j to j – 1
    
set the end of newIndex to j
  end repeat
  
  
–Sort TypeのListを作成(あえて外部から指定する内容でもない)
  
set sortTypes to {}
  
repeat (length of sortIndexes) times
    set the end of sortTypes to "compare:"
  end repeat
  
  
–Sort
  
set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:newIndex ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
  
  
return resList
end sort2DList

★Click Here to Open This Script 

Posted in Color GUI | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Keynote NSArray NSBezierPath NSButton NSColor NSImage NSMenu NSMenuItem NSPopUpButton NSScreen NSTextField NSView NSWindow NSWindowController | 1 Comment

Numbers書類の現在のシート上の表1の背景色を置換 v1

Posted on 1月 31, 2019 by Takaaki Naganoya

Numbersでオープン中の最前面の書類の現在表示中のシートに存在する表1の背景色を置換するAppleScriptです。

Pages用のScriptをごく一部修正してNumbersの表に対して処理できるようにしてみました。macOS 10.11, 10.12, 10.13ではスクリプトエディタ上で動作します。macOS 10.14ではSIPを解除してスクリプトエディタで動かすか、アプレット形式で書き出して、アプレットのバンドル中にdbColNamesKit.frameworkを入れると動きます。

Script DebuggerとScript Menu上では動作しません。

ポップアップメニュー中に色の名称の動的な推定に、オープンソースの「DBColorNames」をフレームワーク化した
「dbColNamesKit.framework」を利用しています。

–> dbColNamesKit.framework (To ~/Library/Frameworks)

AppleScript名:Numbers書類の現在のシート上の表1の背景色を置換 v1
— Created 2019-01-29 by Takaaki Naganoya
— 2019 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/
use Bplus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

–v1:Change Pages part to Numbers

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 NSFloatingWindowLevel : a reference to current application’s NSFloatingWindowLevel
property NSBackingStoreBuffered : a reference to current application’s NSBackingStoreBuffered
property NSMomentaryLightButton : a reference to current application’s NSMomentaryLightButton

property windisp : true
property wController : missing value
property pop1ind : 1

–初期化
set (my windisp) to true
set (my pop1ind) to 1
load framework

–Pagesの1ページ目にある表の塗り色を取得
tell application "Numbers"
  tell front document
    tell active sheet
      tell table 1
        set c1List to background color of every cell
        
set aProp to properties
        
set xCount to column count of aProp
      end tell
    end tell
  end tell
end tell

–Convert 1D List to 2D List
set c3List to (current application’s SMSForder’s subarraysFrom:c1List groupedBy:xCount |error|:(missing value)) as list

–色データをユニーク化(重複削除)
set bList to uniquifyList(c1List) of me

–missing value(背景色なし)を除外する
set c2List to (current application’s SMSForder’s arrayByDeletingBlanksIn:(bList)) as list

–Popup Menuで置換色選択
set paramObj to {c2List, 65535, "OK", "Select Target Color", 180} –Timeout = 180 sec, Color val range = 16bit
my performSelectorOnMainThread:"getPopupValues:" withObject:(paramObj) waitUntilDone:true
if pop1ind = false then return –timed out
set fromCol to (contents of item pop1ind of c2List)

–カラーピッカーで置換色選択
set tCol to choose color default color fromCol

set d1 to current date

–実際に表の背景色を置換する
set hitList to findDataFrom2DList(fromCol, c3List) of me –データ上で当該色のセル情報を計算する

–Rangeを横スキャンと縦スキャンの2通りで試算(Two way Simulation)
set rList1 to retRangeFromPosListHorizontal(hitList) of me –横方向へのrange評価
set rList2 to retRangeFromPosListVertival(hitList) of me –縦方向へのrange評価

–Simulationの結果、要素数の少ない方(=処理時間の短い方=高速な方)を採用する
if (length of rList1) < (length of rList2) then
  copy rList1 to rangeList
else
  copy rList2 to rangeList
end if

tell application "Numbers"
  activate
  
tell front document
    tell active sheet
      tell table 1
        repeat with i in rangeList
          set j to contents of i
          
          
ignoring application responses –非同期実行モードで高速実行
            set background color of range j to tCol
          end ignoring
          
        end repeat
      end tell
    end tell
  end tell
end tell

set d2 to current date
return d2 – d1

–カラーポップアップメニューをウィンドウ表示
on getPopupValues:paramObj
  copy (paramObj as list) to {ap1List, aColMax, aButtonMSG, aSliderValMSG, 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 aCDB to current application’s DBColorNames’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 makeRoundedNSImageWithFilledWithColor(64, 64, nsCol, 4) of me
    
    
set aTitle to "#" & (iCount as string) & " " & (aCDB’s nameForColor:nsCol) 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:(NSMomentaryLightButton)
  
bButton’s setBezelStyle:(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, aSliderValMSG))
  
  
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 number) + 1)
  else
    set s1Val to false
  end if
  
  
copy s1Val to my pop1ind
  
end getPopupValues:

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

–make Window for Display
on makeWinWithView(aView, aWinWidth as integer, aWinHeight as integer, aTitle as string)
  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:(NSFloatingWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
  
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:

–aMaxValを最大値とする数値でNSColorを作成して返す
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

–指定サイズのNSImageを作成し、指定色で塗ってNSImageで返す
on makeNSImageWithFilledWithColor(aWidth as integer, aHeight as integer, 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

–指定サイズのNSImageを作成し、指定色で塗ってNSImageで返す、anRadiusの半径の角丸で
on makeRoundedNSImageWithFilledWithColor(aWidth as integer, aHeight as integer, fillColor, anRadius as real)
  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 bezierPathWithRoundedRect:theRect xRadius:anRadius yRadius:anRadius
  
—
  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  
—
  
anImage’s unlockFocus()
  
  
return anImage
end makeRoundedNSImageWithFilledWithColor

on uniquifyList(aList as list)
  set aArray to NSArray’s arrayWithArray:aList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
return bArray as list
end uniquifyList

on findDataFrom2DList(anItem, aList as list)
  script spd
    property aList : {}
    
property resList : {}
  end script
  
  
set (aList of spd) to aList
  
set (resList of spd) to {}
  
  
set yCount to 1
  
  
repeat with i in (aList of spd)
    
    
set aResList to (Bplus’s indexesOfItem:anItem inList:i inverting:false) as list
    
    
set tmpList to {}
    
if aResList is not equal to {} then
      repeat with ii in aResList
        set jj to contents of ii
        
set the end of tmpList to {jj, yCount}
      end repeat
      
set (resList of spd) to (resList of spd) & tmpList
    end if
    
    
set yCount to yCount + 1
  end repeat
  
  
return (resList of spd) –return {{x, y}…..} item list (1-based)
end findDataFrom2DList

on retRangeFromPosListVertival(posList as list)
  script rangeSPD
    property posList2 : {}
  end script
  
  
–縦方向へのrange評価に都合がいいようにソート
  
set (posList2 of rangeSPD) to shellSortListAscending(posList, {1, 2}) of me
  
  
–先頭データをピックアップ
  
set firstData to first item of (posList2 of rangeSPD)
  
set (posList2 of rangeSPD) to rest of (posList2 of rangeSPD)
  
  
copy firstData to {curX1, curY1}
  
set tmpRangeStr to aNumToExcelColumn(curX1) of me & (curY1 as string) & ":"
  
  
set tmpRange to {}
  
set hitF to false
  
  
set outList to {}
  
  
repeat with i in (posList2 of rangeSPD)
    copy i to {tmpX, tmpY}
    
    
–log {"{curX1, curY1}", {curX1, curY1}}
    
–log {"{tmpX, tmpY}", {tmpX, tmpY}}
    
    
    
if (curX1 = tmpX) and (curY1 + 1 = tmpY) then
      –Y方向への連続値を拾っている最中
      
if hitF = false then
        –log "case 1a"
        
–log {"hitF", hitF}
        
set hitF to true
      else
        –log "case 1b"
        
–log {"hitF", hitF}
        
–横に連続しているブロックの途中
      end if
    else
      –直前の値と連続していない
      
if hitF = false then
        –log "case 2a"
        
–log {"hitF", hitF}
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
      else
        –log "case 2b"
        
–log {"hitF", hitF}
        
–連続ブロックの末尾を拾った
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
        
–log {"tmpRangeStr", tmpRangeStr}
      end if
    end if
    
    
copy {tmpX, tmpY} to {curX1, curY1}
  end repeat
  
  
–log {tmpRangeStr, hitF}
  
  
if (hitF = true) or (tmpRangeStr is not equal to "") then
    set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
    
set the end of outList to tmpRangeStr
  end if
  
  
return outList
end retRangeFromPosListVertival

on retRangeFromPosListHorizontal(posList as list)
  script rangeSPD
    property posList2 : {}
  end script
  
  
copy posList to (posList2 of rangeSPD)
  
  
–先頭データをピックアップ
  
set firstData to first item of (posList2 of rangeSPD)
  
set (posList2 of rangeSPD) to rest of (posList2 of rangeSPD)
  
  
copy firstData to {curX1, curY1}
  
set tmpRangeStr to aNumToExcelColumn(curX1) of me & (curY1 as string) & ":"
  
  
set tmpRange to {}
  
set hitF to false
  
  
set outList to {}
  
  
repeat with i in (posList2 of rangeSPD)
    copy i to {tmpX, tmpY}
    
    
–log {"{curX1, curY1}", {curX1, curY1}}
    
–log {"{tmpX, tmpY}", {tmpX, tmpY}}
    
    
    
if (curX1 + 1 = tmpX) and (curY1 = tmpY) then
      –X方向への連続値を拾っている最中
      
if hitF = false then
        –log "case 1a"
        
–log {"hitF", hitF}
        
set hitF to true
      else
        –log "case 1b"
        
–log {"hitF", hitF}
        
–横に連続しているブロックの途中
      end if
    else
      –直前の値と連続していない
      
if hitF = false then
        –log "case 2a"
        
–log {"hitF", hitF}
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
      else
        –log "case 2b"
        
–log {"hitF", hitF}
        
–連続ブロックの末尾を拾った
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
        
–log {"tmpRangeStr", tmpRangeStr}
      end if
    end if
    
    
copy {tmpX, tmpY} to {curX1, curY1}
  end repeat
  
  
–log {tmpRangeStr, hitF}
  
  
if (hitF = true) or (tmpRangeStr is not equal to "") then
    set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
    
set the end of outList to tmpRangeStr
  end if
  
  
return outList
end retRangeFromPosListHorizontal

–2008/05/01 By Takaaki Naganoya
–10進数数値をExcel 2004/2008的カラム表現にエンコードするサブルーチン を使いまわし
–1〜1351までの間であれば正しいエンコーディング結果を返す
on aNumToExcelColumn(origNum as integer)
  if origNum > 1351 then
    error "エラー:Excel 2004/2008的カラム表現(A1形式)への変換ルーチンにおいて、想定範囲外(1351以上)のパラメータが指定されました"
  end if
  
  
set upperDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
set lowerDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
  
set oNum to origNum
  
set nTh to 26
  
set stringLength to 4
  
  
–数字が1桁の場合の対応
  
if origNum < 27 then
    set aRes to (item origNum of upperDigitEncTable) as string
    
return aRes
  end if
  
  
  
if origNum > 702 then
    –3桁になる場合
    
set upupNum to oNum div 676 –整数除算–上の上の桁
    
set oNum to oNum – (upupNum * 676)
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–超つじつま合わせ処理
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upupChar to (item upupNum of upperDigitEncTable) as string
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upupChar & upChar & lowChar
    
  else
    –2桁の場合
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–超つじつま合わせ処理
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upChar & lowChar
    
  end if
  
  
return resText
end aNumToExcelColumn

–入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  return sort2DList(a, keyItem, {true}) of me
end shellSortListAscending

–入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  return sort2DList(a, keyItem, {false}) of me
end shellSortListDecending

–2D Listをソート
on sort2DList(aList as list, sortIndexes as list, sortOrders as list)
  
  
–index値をAS流(アイテムが1はじまり)からCocoa流(アイテムが0はじまり)に変換
  
set newIndex to {}
  
repeat with i in sortIndexes
    set j to contents of i
    
set j to j – 1
    
set the end of newIndex to j
  end repeat
  
  
–Sort TypeのListを作成(あえて外部から指定する内容でもない)
  
set sortTypes to {}
  
repeat (length of sortIndexes) times
    set the end of sortTypes to "compare:"
  end repeat
  
  
–Sort
  
set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:newIndex ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
  
  
return resList
end sort2DList

★Click Here to Open This Script 

Posted in Color GUI | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSBezierPath NSButton NSColor NSImage NSMenu NSMenuItem NSPopUpButton NSRoundedBezelStyle NSScreen NSTextField NSTitledWindowMask NSView NSWindow NSWindowController Numbers | Leave a comment

Pages書類の1ページ目の表の背景色を置換 v4

Posted on 1月 30, 2019 by Takaaki Naganoya

Pagesでオープン中の最前面の書類の1ページ目に存在する表オブジェクト中の背景色を置換するAppleScriptです。

例によって、Pages書類上の表オブジェクトは「配置」を「移動しない」に設定しないとAppleScriptからアクセスできないので、あらかじめ「移動しない」設定にしておく必要があります。

本バージョンでは、データの連続部分をrangeとしてまとめて処理する方法を、1行ずつ横方向に(左→右)スキャンした場合と、1列ずつ(上→下)縦方向にスキャンした場合の2パターンで処理し、より少ないデータ単位(より多くのセルを一括で指定)で処理できる方の方式を採用するようにしました。

このため、処理データによって塗りつぶし動作が変わってきます。横方向のみのスキャンでは大幅に遅くなるタイプのデータ(縦に処理対象セルが並んでいて、横方向にはつながっていない)でも「遅くならない」ことがテーマです。

ほかには、オープンソースの「DBColorNames」を利用して色から色名称を動的に取得したり、ポップアップメニューに表示する色イメージ画像を角丸にしたりしています。

–> dbColNamesKit.framework (To ~/Library/Frameworks)

これ以上の高速化は、「縦横混在のrangeシミュレーション」でも行わないとできないと思いますが、方法についてはいろいろ思いつくので論理的には可能だと思います。ただ、単に塗りつぶしを高速化するためだけに何日も試行錯誤するのはちょっと………なので、このぐらいにしておこうかと。

macOS 10.14上では、「csrutil disable」でSIPをオフにすると、ホームディレクトリ下の~/Library/Frameworksフォルダ内のFrameworkにアクセスでき、本Scriptをそのままスクリプトエディタで実行できました。アプレット形式で書き出して、アプレット内にFrameworkを入れればSIPをオフにしなくても実行できるはずです。Script Debugger/Script Menu上では実行できないというあたりが困りどころです。

AppleScript名:Pages書類の1ページ目の表の背景色を置換 v4.1
— Created 2017-07-15 by Takaaki Naganoya
— Modified 2019-01-29 by Takaaki Naganoya
— 2019 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/
use Bplus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

–v1:First Version
–v2:Pick Up target cells by calculate every background color (35% speed up)
–v3:Draw cells by range (x20 speed up)
–v3.1:Bug Fix (retRangeFromPosList)
–v3.1.1:Bug Fix (retRangeFromPosList)
–v4:Two-way Simulation (Horizontal / Vertical scan), Corner-Rounded NSImage, Dynamic Color Naming
–v4.1:Correct Vertical Scan simulation (Sort list for vertical range evaluation, at first)

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 NSFloatingWindowLevel : a reference to current application’s NSFloatingWindowLevel
property NSBackingStoreBuffered : a reference to current application’s NSBackingStoreBuffered
property NSMomentaryLightButton : a reference to current application’s NSMomentaryLightButton

property windisp : true
property wController : missing value
property pop1ind : 1

–初期化
set (my windisp) to true
set (my pop1ind) to 1
load framework

–Pagesの1ページ目にある表の塗り色を取得
tell application "Pages"
  tell front document
    tell table 1
      set c1List to background color of every cell
      
set aProp to properties
      
set xCount to column count of aProp
    end tell
  end tell
end tell

–色データをユニーク化(重複削除)
set bList to uniquifyList(c1List) of me

–Convert 1D List to 2D List
set c3List to (current application’s SMSForder’s subarraysFrom:c1List groupedBy:xCount |error|:(missing value)) as list

–missing value(背景色なし)を除外する
set c2List to (current application’s SMSForder’s arrayByDeletingBlanksIn:(bList)) as list

–Popup Menuで置換色選択
set paramObj to {c2List, 65535, "OK", "Select Target Color", 180} –Timeout = 180 sec, Color val range = 16bit
my performSelectorOnMainThread:"getPopupValues:" withObject:(paramObj) waitUntilDone:true
if pop1ind = false then return –timed out
set fromCol to (contents of item pop1ind of c2List)

–カラーピッカーで置換色選択
set tCol to choose color default color fromCol

set d1 to current date

–実際に表の背景色を置換する
set hitList to findDataFrom2DList(fromCol, c3List) of me –データ上で当該色のセル情報を計算する

–Rangeを横スキャンと縦スキャンの2通りで試算(Two way Simulation)
set rList1 to retRangeFromPosListHorizontal(hitList) of me –横方向へのrange評価
set rList2 to retRangeFromPosListVertival(hitList) of me –縦方向へのrange評価

–Simulationの結果、要素数の少ない方(=処理時間の短い方=高速な方)を採用する
log {"Simulation", (length of rList1), (length of rList2)}
if (length of rList1) < (length of rList2) then
  copy rList1 to rangeList
else
  copy rList2 to rangeList
end if

tell application "Pages"
  activate
  
tell front document
    tell table 1
      repeat with i in rangeList
        set j to contents of i
        
        
ignoring application responses –非同期実行モードで高速実行
          set background color of range j to tCol
        end ignoring
        
      end repeat
    end tell
  end tell
end tell

set d2 to current date
return d2 – d1

–カラーポップアップメニューをウィンドウ表示
on getPopupValues:paramObj
  copy (paramObj as list) to {ap1List, aColMax, aButtonMSG, aSliderValMSG, 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 aCDB to current application’s DBColorNames’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 makeRoundedNSImageWithFilledWithColor(64, 64, nsCol, 4) of me
    
    
set aTitle to "#" & (iCount as string) & " " & (aCDB’s nameForColor:nsCol) 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:(NSMomentaryLightButton)
  
bButton’s setBezelStyle:(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, aSliderValMSG))
  
  
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 number) + 1)
  else
    set s1Val to false
  end if
  
  
copy s1Val to my pop1ind
  
end getPopupValues:

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

–make Window for Display
on makeWinWithView(aView, aWinWidth as integer, aWinHeight as integer, aTitle as string)
  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:(NSFloatingWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
  
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:

–aMaxValを最大値とする数値でNSColorを作成して返す
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

–指定サイズのNSImageを作成し、指定色で塗ってNSImageで返す
on makeNSImageWithFilledWithColor(aWidth as integer, aHeight as integer, 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

–指定サイズのNSImageを作成し、指定色で塗ってNSImageで返す、anRadiusの半径の角丸で
on makeRoundedNSImageWithFilledWithColor(aWidth as integer, aHeight as integer, fillColor, anRadius as real)
  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 bezierPathWithRoundedRect:theRect xRadius:anRadius yRadius:anRadius
  
—
  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  
—
  
anImage’s unlockFocus()
  
  
return anImage
end makeRoundedNSImageWithFilledWithColor

on uniquifyList(aList as list)
  set aArray to NSArray’s arrayWithArray:aList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
return bArray as list
end uniquifyList

on findDataFrom2DList(anItem, aList as list)
  script spd
    property aList : {}
    
property resList : {}
  end script
  
  
set (aList of spd) to aList
  
set (resList of spd) to {}
  
  
set yCount to 1
  
  
repeat with i in (aList of spd)
    
    
set aResList to (Bplus’s indexesOfItem:anItem inList:i inverting:false) as list
    
    
set tmpList to {}
    
if aResList is not equal to {} then
      repeat with ii in aResList
        set jj to contents of ii
        
set the end of tmpList to {jj, yCount}
      end repeat
      
set (resList of spd) to (resList of spd) & tmpList
    end if
    
    
set yCount to yCount + 1
  end repeat
  
  
return (resList of spd) –return {{x, y}…..} item list (1-based)
end findDataFrom2DList

on retRangeFromPosListVertival(posList as list)
  script rangeSPD
    property posList2 : {}
  end script
  
  
–縦方向へのrange評価に都合がいいようにソート
  
set (posList2 of rangeSPD) to shellSortListAscending(posList, {1, 2}) of me
  
  
–先頭データをピックアップ
  
set firstData to first item of (posList2 of rangeSPD)
  
set (posList2 of rangeSPD) to rest of (posList2 of rangeSPD)
  
  
copy firstData to {curX1, curY1}
  
set tmpRangeStr to aNumToExcelColumn(curX1) of me & (curY1 as string) & ":"
  
  
set tmpRange to {}
  
set hitF to false
  
  
set outList to {}
  
  
repeat with i in (posList2 of rangeSPD)
    copy i to {tmpX, tmpY}
    
    
–log {"{curX1, curY1}", {curX1, curY1}}
    
–log {"{tmpX, tmpY}", {tmpX, tmpY}}
    
    
    
if (curX1 = tmpX) and (curY1 + 1 = tmpY) then
      –Y方向への連続値を拾っている最中
      
if hitF = false then
        –log "case 1a"
        
–log {"hitF", hitF}
        
set hitF to true
      else
        –log "case 1b"
        
–log {"hitF", hitF}
        
–横に連続しているブロックの途中
      end if
    else
      –直前の値と連続していない
      
if hitF = false then
        –log "case 2a"
        
–log {"hitF", hitF}
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
      else
        –log "case 2b"
        
–log {"hitF", hitF}
        
–連続ブロックの末尾を拾った
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
        
–log {"tmpRangeStr", tmpRangeStr}
      end if
    end if
    
    
copy {tmpX, tmpY} to {curX1, curY1}
  end repeat
  
  
–log {tmpRangeStr, hitF}
  
  
if (hitF = true) or (tmpRangeStr is not equal to "") then
    set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
    
set the end of outList to tmpRangeStr
  end if
  
  
return outList
end retRangeFromPosListVertival

on retRangeFromPosListHorizontal(posList as list)
  script rangeSPD
    property posList2 : {}
  end script
  
  
copy posList to (posList2 of rangeSPD)
  
  
–先頭データをピックアップ
  
set firstData to first item of (posList2 of rangeSPD)
  
set (posList2 of rangeSPD) to rest of (posList2 of rangeSPD)
  
  
copy firstData to {curX1, curY1}
  
set tmpRangeStr to aNumToExcelColumn(curX1) of me & (curY1 as string) & ":"
  
  
set tmpRange to {}
  
set hitF to false
  
  
set outList to {}
  
  
repeat with i in (posList2 of rangeSPD)
    copy i to {tmpX, tmpY}
    
    
–log {"{curX1, curY1}", {curX1, curY1}}
    
–log {"{tmpX, tmpY}", {tmpX, tmpY}}
    
    
    
if (curX1 + 1 = tmpX) and (curY1 = tmpY) then
      –X方向への連続値を拾っている最中
      
if hitF = false then
        –log "case 1a"
        
–log {"hitF", hitF}
        
set hitF to true
      else
        –log "case 1b"
        
–log {"hitF", hitF}
        
–横に連続しているブロックの途中
      end if
    else
      –直前の値と連続していない
      
if hitF = false then
        –log "case 2a"
        
–log {"hitF", hitF}
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
      else
        –log "case 2b"
        
–log {"hitF", hitF}
        
–連続ブロックの末尾を拾った
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
        
–log {"tmpRangeStr", tmpRangeStr}
      end if
    end if
    
    
copy {tmpX, tmpY} to {curX1, curY1}
  end repeat
  
  
–log {tmpRangeStr, hitF}
  
  
if (hitF = true) or (tmpRangeStr is not equal to "") then
    set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
    
set the end of outList to tmpRangeStr
  end if
  
  
return outList
end retRangeFromPosListHorizontal

–2008/05/01 By Takaaki Naganoya
–10進数数値をExcel 2004/2008的カラム表現にエンコードするサブルーチン を使いまわし
–1〜1351までの間であれば正しいエンコーディング結果を返す
on aNumToExcelColumn(origNum as integer)
  if origNum > 1351 then
    error "エラー:Excel 2004/2008的カラム表現(A1形式)への変換ルーチンにおいて、想定範囲外(1351以上)のパラメータが指定されました"
  end if
  
  
set upperDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
set lowerDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
  
set oNum to origNum
  
set nTh to 26
  
set stringLength to 4
  
  
–数字が1桁の場合の対応
  
if origNum < 27 then
    set aRes to (item origNum of upperDigitEncTable) as string
    
return aRes
  end if
  
  
  
if origNum > 702 then
    –3桁になる場合
    
set upupNum to oNum div 676 –整数除算–上の上の桁
    
set oNum to oNum – (upupNum * 676)
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–超つじつま合わせ処理
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upupChar to (item upupNum of upperDigitEncTable) as string
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upupChar & upChar & lowChar
    
  else
    –2桁の場合
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–超つじつま合わせ処理
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upChar & lowChar
    
  end if
  
  
return resText
end aNumToExcelColumn

–入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  return sort2DList(a, keyItem, {true}) of me
end shellSortListAscending

–入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  return sort2DList(a, keyItem, {false}) of me
end shellSortListDecending

–2D Listをソート
on sort2DList(aList as list, sortIndexes as list, sortOrders as list)
  
  
–index値をAS流(アイテムが1はじまり)からCocoa流(アイテムが0はじまり)に変換
  
set newIndex to {}
  
repeat with i in sortIndexes
    set j to contents of i
    
set j to j – 1
    
set the end of newIndex to j
  end repeat
  
  
–Sort TypeのListを作成(あえて外部から指定する内容でもない)
  
set sortTypes to {}
  
repeat (length of sortIndexes) times
    set the end of sortTypes to "compare:"
  end repeat
  
  
–Sort
  
set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:newIndex ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
  
  
return resList
end sort2DList

★Click Here to Open This Script 

Posted in Color GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSBezierPath NSButton NSColor NSImage NSMenu NSMenuItem NSPopUpButton NSScreen NSTextField NSView NSWindow NSWindowController Pages | 2 Comments

Pages書類の1ページ目の表の背景色を置換 v3.1(約20倍速)

Posted on 1月 29, 2019 by Takaaki Naganoya

Pagesでオープン中の最前面の書類の1ページ目に存在する表オブジェクト中の背景色を置換するAppleScriptの高速化版です。

初版では表中のセルをすべて取得し、すべてのセルについて順次背景色を求め、置換対象色であれば塗り直していました。セル1個ずつ塗っていたので、それなりの時間(テストデータでは20秒)がかかっていました。GUIアプリケーションを操作するために存在しているAppleScriptですが、アプリケーションとの通信はコストが高いので、この通信部分を減らすことが高速化の基本です。

ちなみに、「ignoring application responses」〜「end ignoring」で囲った範囲は非同期実行モードで実行され、2.5倍ぐらい高速になります。AppleScriptでは、GUIアプリケーションに対してコマンドを実行した場合、「コマンド実行」「コマンド処理」「コマンド結果受信」の3ステップを行なっています。非同期実行モードでは「コマンド実行」だけを行うので高速ですが、設定値や状態を知りたいような場合には(結果が返ってこないので)使う意味がありません。本Blogを「ignoring application responses」で検索しても、あまり出てこないはずです。

v2では、高速化のために全セルの背景色を抽出したあとに、2次元配列データ上で当該セルの座標を計算し、塗り替え対象セルのみ処理することで約35%のスピードアップを行いました。GUIアプリケーションにコマンドを送る(そして、処理を待って結果を受信する)よりも配列変数上で検索を行ったほうがはるかに高速です。

本v3では、さらなる高速化のために、2次元配列データ上で当該セルの座標を計算したあと、それらを横方向にスキャンして、点ではなく範囲(range)として評価し、極力複数のセルを連続する範囲(range)としてまとめて、rangeを一括塗りすることで初版から約20倍の高速化を行なったものです。v1やv2ではセルを1つずつ塗りつぶしていましたが、v3からは横方向に複数まとめてぬりつぶします。

実際のPages書類上の表の塗りつぶし領域が、ヘッダーや年表のような横方向に連続した塗りエリアを持つことが多いことに着目して、複数セルを一度に塗りつぶすことで高速化しました(1セル塗るのと複数セルを塗るのとで時間かわらず)。本Scriptでも1マスごとに塗りつぶしが存在するようなハッチング模様の表の塗りつぶし色を変更する場合にはパフォーマンスが最悪の状態に落ち込みますが、v2のレベル以下に落ちることはないでしょう。

この世のどこかには、さらにデータを1D Arrayではなく2D Arrayとして評価して、最大限まとめた面積のrangeとみなすことで本ルーチンの2倍ぐらいの速度で処理できるプログラムも存在しそうですが、人類の叡智のレベルを超えそうなので、自分はこのぐらいの速度が出れば満足です(総当たり的なアプローチで全パターンをシミュレーションして、一番いい結果が出るものを採用というのも考えないではないですが、シミュレーションをていねいに行うと処理時間が余計にかかるので、、、)。

1D Listに入っている座標値をX軸方向に評価して、連続値であればrangeに変換するルーチン「retRangeFromPosList」については、それほど真剣にテストしていないので処理が間違っている可能性もあります。
→ 処理に誤りがあったので修正しました(v3.1)

また、数値からExcelのA1形式のアドレスに変換するために作ってホコリをかぶっていた「aNumToExcelColumn」ルーチンも、一応テストはしてありますが変換できるアドレスの上限値が存在しているため注意が必要です。

AppleScript名:Pages書類の1ページ目の表の背景色を置換 v3.11
— Created 2017-07-15 by Takaaki Naganoya
— Modified 2019-01-29 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use Bplus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

–v1:First Version
–v2:Pick Up target cells by calculate every background color (35% speed up)
–v3:Draw cells by range (x20 speed up)
–v3.1:Bug Fix (retRangeFromPosList)
–v3.1.1:Bug Fix (retRangeFromPosList)

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 : true
property wController : missing value
property pop1ind : 1

–初期化
set (my windisp) to true
set (my pop1ind) to 1
load framework

–Pagesの1ページ目にある表の塗り色を取得
tell application "Pages"
  tell front document
    tell table 1
      set c1List to background color of every cell
      
set aProp to properties
      
set xCount to column count of aProp
    end tell
  end tell
end tell

–色データをユニーク化(重複削除)
set bList to uniquifyList(c1List) of me

–Convert 1D List to 2D List
set c3List to (current application’s SMSForder’s subarraysFrom:c1List groupedBy:xCount |error|:(missing value)) as list

–missing value(背景色なし)を除外する
load framework
set c2List to (current application’s SMSForder’s arrayByDeletingBlanksIn:(bList)) as list

–Popup Menuで置換色選択
set aButtonMSG to "OK"
set aSliderValMSG to "Select Target Color"
set paramObj to {c2List, 65535, aButtonMSG, aSliderValMSG, 20}
my performSelectorOnMainThread:"getPopupValues:" withObject:(paramObj) waitUntilDone:true
set fromCol to (contents of item pop1ind of c2List)

–カラーピッカーで置換色選択
set tCol to choose color default color fromCol

set d1 to current date

–実際に表の背景色を置換する
set hitList to findDataFrom2DList(fromCol, c3List) of me –データ上で当該色のセル情報を計算する

set rangeList to retRangeFromPosList(hitList) of me

tell application "Pages"
  tell front document
    tell table 1
      repeat with i in rangeList
        set j to contents of i
        
        
ignoring application responses –非同期実行モードで高速実行
          set background color of range j to tCol
        end ignoring
      end repeat
    end tell
  end tell
end tell

set d2 to current date
return d2 – d1

–カラーポップアップメニューをウィンドウ表示
on getPopupValues:paramObj
  copy (paramObj as list) to {ap1List, aColMax, aButtonMSG, aSliderValMSG, 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:(NSMomentaryLightButton)
  
bButton’s setBezelStyle:(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, aSliderValMSG))
  
  
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 number) + 1)
  else
    set s1Val to false
  end if
  
  
copy s1Val to my pop1ind
  
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 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 setReleasedWhenClosed:true
  
aWin’s |center|()
  
  
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:

–aMaxValを最大値とする数値でNSColorを作成して返す
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

–指定サイズのNSImageを作成し、指定色で塗って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 uniquifyList(aList as list)
  set aArray to NSArray’s arrayWithArray:aList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
return bArray as list
end uniquifyList

on findDataFrom2DList(anItem, aList)
  script spd
    property aList : {}
    
property resList : {}
  end script
  
  
set (aList of spd) to aList
  
set (resList of spd) to {}
  
  
set yCount to 1
  
  
repeat with i in (aList of spd)
    
    
set aResList to (Bplus’s indexesOfItem:anItem inList:i inverting:false) as list
    
    
set tmpList to {}
    
if aResList is not equal to {} then
      repeat with ii in aResList
        set jj to contents of ii
        
set the end of tmpList to {jj, yCount}
      end repeat
      
set (resList of spd) to (resList of spd) & tmpList
    end if
    
    
set yCount to yCount + 1
  end repeat
  
  
return (resList of spd) –return {{x, y}…..} item list (1-based)
end findDataFrom2DList

on retRangeFromPosList(posList as list)
  script rangeSPD
    property posList2 : {}
  end script
  
  
copy posList to (posList2 of rangeSPD)
  
  
–先頭データをピックアップ
  
set firstData to first item of (posList2 of rangeSPD)
  
set (posList2 of rangeSPD) to rest of (posList2 of rangeSPD)
  
  
copy firstData to {curX1, curY1}
  
set tmpRangeStr to aNumToExcelColumn(curX1) of me & (curY1 as string) & ":"
  
  
set tmpRange to {}
  
set hitF to false
  
  
set outList to {}
  
  
repeat with i in (posList2 of rangeSPD)
    copy i to {tmpX, tmpY}
    
    
–log {"{curX1, curY1}", {curX1, curY1}}
    
–log {"{tmpX, tmpY}", {tmpX, tmpY}}
    
    
    
if (curX1 + 1 = tmpX) and (curY1 = tmpY) then
      –X方向への連続値を拾っている最中
      
if hitF = false then
        –log "case 1a"
        
–log {"hitF", hitF}
        
set hitF to true
      else
        –log "case 1b"
        
–log {"hitF", hitF}
        
–横に連続しているブロックの途中
      end if
    else
      –直前の値と連続していない
      
if hitF = false then
        –log "case 2a"
        
–log {"hitF", hitF}
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
      else
        –log "case 2b"
        
–log {"hitF", hitF}
        
–連続ブロックの末尾を拾った
        
set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
        
set the end of outList to tmpRangeStr
        
set tmpRangeStr to aNumToExcelColumn(tmpX) of me & (tmpY as string) & ":"
        
set hitF to false
        
–log {"tmpRangeStr", tmpRangeStr}
      end if
    end if
    
    
copy {tmpX, tmpY} to {curX1, curY1}
  end repeat
  
  
–log {tmpRangeStr, hitF}
  
  
if (hitF = true) or (tmpRangeStr is not equal to "") then
    set tmpRangeStr to tmpRangeStr & aNumToExcelColumn(curX1) of me & (curY1 as string)
    
set the end of outList to tmpRangeStr
  end if
  
  
return outList
end retRangeFromPosList

–2008/05/01 By Takaaki Naganoya
–10進数数値をExcel 2004/2008的カラム表現にエンコードするサブルーチン を使いまわし
–1〜1351までの間であれば正しいエンコーディング結果を返す
on aNumToExcelColumn(origNum as integer)
  if origNum > 1351 then
    display dialog "エラー:Excel 2004/2008的カラム表現(A1形式)への変換ルーチンにおいて、想定範囲外(1351以上)のパラメータが指定されました" buttons {"OK"} default button 1
    
return ""
  end if
  
  
set upperDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
set lowerDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
  
set oNum to origNum
  
set nTh to 26
  
set stringLength to 4
  
  
–数字が1桁の場合の対応
  
if origNum < 27 then
    set aRes to (item origNum of upperDigitEncTable) as string
    
return aRes
  end if
  
  
  
if origNum > 702 then
    –3桁になる場合
    
set upupNum to oNum div 676 –整数除算–上の上の桁
    
set oNum to oNum – (upupNum * 676)
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–超つじつま合わせ処理
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upupChar to (item upupNum of upperDigitEncTable) as string
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upupChar & upChar & lowChar
    
  else
    –2桁の場合
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–超つじつま合わせ処理
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upChar & lowChar
    
  end if
  
  
return resText
end aNumToExcelColumn

★Click Here to Open This Script 

Posted in Color GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSBackingStoreBuffered NSBezierPath NSButton NSColor NSImage NSMenu NSMenuItem NSMomentaryLightButton NSNormalWindowLevel NSPopUpButton NSRoundedBezelStyle NSScreen NSTextField NSTitledWindowMask NSView NSWindow NSWindowController Pages | Leave a comment

Pages書類の1ページ目の表の背景色を置換

Posted on 1月 28, 2019 by Takaaki Naganoya

Pagesでオープン中の最前面の書類の1ページ目に存在する表オブジェクト中の背景色を置換するAppleScriptです。

Pages書類の「表オブジェクト」にAppleScriptからアクセスするためには、Pages上で表オブジェクトを選択した状態で、

「オブジェクトの配置」を「移動しない」に事前に設定しておく必要があります。

本AppleScriptを実行可能なランタイム環境は、スクリプトエディタ、アプレットの2つです。スクリプトメニューおよびScript Debuggerでは各GUI部品のクリックイベントを拾えないために、実行しても選択などが行えません。

のように、1ページのみのPages書類に表オブジェクトを入れて、「移動しない」設定にしてある状態で本AppleScriptを実行。

表のセル中の色をすべて取得して、ユニーク化してポップアップメニューから「変更元」の色を選択できます。

選択して、「OK」ボタンをクリック。ここで選択した色が置換されます。

選択した色をどの色に変更(置換)するのかを指定します。

黒い色を選択。

ちょっと置換に時間がかかりますが(テストデータ+開発環境で20秒前後)、セルの背景色をします。このあたり、Pagesの表オブジェクト中のセルに対して、background colorでフィルタ参照できればよかったのですが、自分が試した範囲ではフィルタ参照で抽出できませんでした。全セルを取得してループで背景色のチェックと置換を行なっています。

こういう機能が最初からPagesに実装されているわけでもないので、手作業よりははるかに高速なのでしばらく放っておけば終わるのでいいんじゃないでしょうか。

本来であれば、指定色に該当するセルをフィルタ参照で抽出して、ヒットしたセルだけでループして背景色を変更できるとベストですが、実際にフィルタ参照で抽出できるかどうかはアプリケーション側の対応度次第なので、実際にやってみないとわかりません。

AppleScript名:Pages書類の1ページ目の表の背景色を置換
— Created 2017-07-15 by Takaaki Naganoya
— Created 2019-01-28 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use Bplus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

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
property wController : false
property pop1ind : missing value

–Pagesの1ページ目にある表の塗り色を取得
tell application "Pages"
  tell front document
    tell table 1
      set cList to background color of every cell
    end tell
  end tell
end tell

–色データをユニーク化(重複削除)
set bList to uniquifyList(cList) of me

–missing value(背景色なし)を除外する
load framework
set cList to (current application’s SMSForder’s arrayByDeletingBlanksIn:(bList)) as list

–Popup Menuで置換色選択
set aButtonMSG to "OK"
set aSliderValMSG to "Select Target Color"
set paramObj to {cList, 65535, aButtonMSG, aSliderValMSG, 20}
my performSelectorOnMainThread:"getPopupValues:" withObject:(paramObj) waitUntilDone:true

–カラーピッカーで色選択
set fromCol to (contents of item pop1ind of cList)
set tCol to choose color default color fromCol

–実際に表の背景色を置換する
tell application "Pages"
  tell front document
    tell table 1
      set cellList to every cell
      
      
repeat with i in cellList
        set aBG to background color of i
        
        
if aBG = fromCol then
          ignoring application responses
            set background color of i to tCol
          end ignoring
        end if
      end repeat
    end tell
  end tell
end tell

–カラーポップアップメニューをウィンドウ表示
on getPopupValues:paramObj
  copy (paramObj as list) to {ap1List, aColMax, aButtonMSG, aSliderValMSG, 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 0
  
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:(NSMomentaryLightButton)
  
bButton’s setBezelStyle:(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, aSliderValMSG))
  
  
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 number) + 1)
  else
    set s1Val to false
  end if
  
  
copy s1Val to my pop1ind
  
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 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 setReleasedWhenClosed:true
  
aWin’s |center|()
  
  
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:

–aMaxValを最大値とする数値でNSColorを作成して返す
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

–指定サイズのNSImageを作成し、指定色で塗って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 uniquifyList(aList as list)
  set aArray to NSArray’s arrayWithArray:aList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
return bArray as list
end uniquifyList

★Click Here to Open This Script 

Posted in Color GUI | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSBackingStoreBuffered NSBezierPath NSButton NSColor NSImage NSMenu NSMenuItem NSMomentaryLightButton NSNormalWindowLevel NSPopUpButton NSRoundedBezelStyle NSScreen NSTextField NSTitledWindowMask NSView NSWindow NSWindowController Pages | 1 Comment

Finder上で選択中の画像のうち、最小のものに合わせて各画像の右上を基準にサイズ統一

Posted on 1月 26, 2019 by Takaaki Naganoya

Finderで選択中の画像のうち、最小のものに合わせて各画像の右上を原点にサイズを統一するAppleScriptです。

Finderで選択中のファイルから画像のみ抽出し、そのうちサイズが最小のものに合わせて他の画像をトリミングし、変更したものをデスクトップフォルダに出力します。

以前に画像の左上を基準に自動トリミングするバージョンのScriptを作成しましたが、あれはメニュー操作の説明用スクリーンショットの大きさを統一するためのもの。右上基準の本バージョンは、Driveのリネームやステータスバーの操作内容を説明するためのスクリーンショットの大きさ統一のために用意したものです。


▲大きさを揃える画像をFinder上で選択


▲処理後の画像。大きさが最小の画像に合わせてそろっている。切り抜き基準は右上

スクリーンショット画像を複数撮った場合に、厳密に同じサイズに固定することは(各種スクリーンショット作成ユーティリティを使わないと)行いづらいところです。

そこで、最小の画像を計算で求めて、それに合わせて自動トリミングするようにしてみました。Cocoaの機能を用いて画像処理しているため、Photoshopは必要ありません。

最小の画像を求めるのに、幅+高さの値でソートして最小のものを求めています(widthとheightによる2 key sortではなく1 Keyで済ませたかったので)。

AppleScript名:Finder上で選択中の画像のうち、最小のものに合わせて各画像の右上を基準にサイズ統一
— Created 2018-01-26 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "QuartzCore"

property |NSURL| : a reference to current application’s |NSURL|
property NSUUID : a reference to current application’s NSUUID
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 NSScreen : a reference to current application’s NSScreen
property NSZeroRect : a reference to current application’s NSZeroRect
property NSPredicate : a reference to current application’s NSPredicate
property NSPNGFileType : a reference to current application’s NSPNGFileType
property NSMutableArray : a reference to current application’s NSMutableArray
property NSCompositeCopy : a reference to current application’s NSCompositeCopy
property NSGraphicsContext : a reference to current application’s NSGraphicsContext
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey
property NSCalibratedRGBColorSpace : a reference to current application’s NSCalibratedRGBColorSpace

tell application "Finder"
  set selList to selection as alias list
end tell

–指定のAlias listのうち画像のみ抽出
set filRes1 to filterAliasListByUTI(selList, "public.image") of me
if filRes1 = {} then
  tell current application
    –Error Message (No Selection)
    
display dialog "Finder上で選択されているファイルはありません。" buttons {"OK"} default button 1 with icon 1 with title "画像リサイズ処理できません"
  end tell
  
return
end if

–各種情報を入れたArrayを作成
set anArray to NSMutableArray’s new()
repeat with i in selList
  set imgPath to (POSIX path of i)
  
  
set aImage to makeNSImageFromFile(i) of me
  
set sizeInfo to |size|() of aImage
  
set sizeInfo to sizeInfo & {aImg:aImage, total:(width of sizeInfo) + (height of sizeInfo), myPath:imgPath}
  (
anArray’s addObject:sizeInfo)
end repeat

–最小のサイズの画像の算出
set aRes to anArray’s valueForKeyPath:("@min.total")
set bRes to first item of (filterRecListByLabel(anArray, "total == " & aRes as string) of me) –最小サイズの画像

–最小サイズ画像のwidthとheight
set minWidth to bRes’s width
set minHeight to bRes’s height

–環境情報の取得
set aPath to POSIX path of (path to desktop)
set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
–>  2.0 (Retina) / 1.0 (Non Retina)

set cRes to filterRecListByLabel(anArray, "total != " & aRes as string) of me –最小サイズ以外の画像

repeat with i in cRes
  set j to contents of i
  
set anImage to aImg of j
  
set fRes to myPath of j
  
  
set tmpSize to |size|() of anImage
  
set tmpWidth to (width of tmpSize)
  
set tmpHeight to (height of tmpSize)
  
  
set cropedImage to (my cropNSImageBy:{(tmpWidth – minWidth), 0, minWidth, minHeight} fromImage:anImage)
  
  
–Retina環境対策
  
if retinaF > 1.0 then
    set cropedImage to (my resizedImage:cropedImage toScale:(1.0))
  end if
  
  
–ファイル書き込み
  
set fRes to retUUIDfilePathFromDir(aPath, "png") of me
  
set sRes to saveNSImageAtPathAsPNG(cropedImage, fRes) of me
end repeat

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel(aRecList as list, aPredicate as string)
  –ListからNSArrayへの型変換
  
set aArray to NSArray’s arrayWithArray:aRecList
  
  
–抽出
  
set aPredicate to NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
  
–NSArrayからListに型変換して返す
  
set bList to filteredArray as list
  
return bList
end filterRecListByLabel

–Alias listから指定UTIに含まれるものをPOSIX pathのリストで返す
on filterAliasListByUTI(aList, targUTI)
  set newList to {}
  
repeat with i in aList
    set j to POSIX path of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, targUTI)
    
if utiRes is not equal to {} then
      set the end of newList to j
    end if
  end repeat
  
return newList
end filterAliasListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

on cropNSImageTo:{x1, y1, x2, y2} fromImage:theImage
  set newWidth to x2 – x1
  
set newHeight to y2 – y1
  
set theSize to (theImage’s |size|()) as record
  
set oldHeight to height of theSize
  
  
— transpose y value for Cocoa coordintates
  
set y1 to oldHeight – newHeight – y1
  
set newRect to {{x:x1, y:y1}, {width:newWidth, height:newHeight}}
  
theImage’s lockFocus()
  
set theRep to NSBitmapImageRep’s alloc()’s initWithFocusedViewRect:newRect
  
theImage’s unlockFocus()
  
  
set outImage to NSImage’s alloc()’s initWithSize:(theRep’s |size|())
  
outImage’s addRepresentation:theRep
  
  
return outImage
end cropNSImageTo:fromImage:

on makeNSImageFromFile(anAlias)
  set imgPath to (POSIX path of anAlias)
  
set aURL to (|NSURL|’s fileURLWithPath:(imgPath))
  
return (NSImage’s alloc()’s initWithContentsOfURL:aURL)
end makeNSImageFromFile

–NSImageを指定の大きさでトリミング
on cropNSImageBy:{x1, y1, newWidth, newHeight} fromImage:theImage
  set theSize to (theImage’s |size|()) as record
  
set oldHeight to height of theSize
  
  
— transpose y value for Cocoa coordintates
  
set y1 to oldHeight – newHeight – y1
  
set newRect to {{x:x1, y:y1}, {width:newWidth, height:newHeight}}
  
theImage’s lockFocus()
  
set theRep to NSBitmapImageRep’s alloc()’s initWithFocusedViewRect:newRect
  
theImage’s unlockFocus()
  
  
set outImage to NSImage’s alloc()’s initWithSize:(theRep’s |size|())
  
outImage’s addRepresentation:theRep
  
  
return outImage
end cropNSImageBy:fromImage:

on retUUIDfilePathFromDir(aPath, aEXT)
  set aUUIDstr to (NSUUID’s UUID()’s UUIDString()) as string
  
set aPath to ((NSString’s stringWithString:aPath)’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:aEXT
  
return aPath
end retUUIDfilePathFromDir

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

on resizedImage:aSourceImg toScale:imgScale
  if (aSourceImg’s isValid()) as boolean = false then error "Invalid NSImage"
  
  
set aSize to aSourceImg’s |size|()
  
–>  {​​​​​width:32.0, ​​​​​height:32.0​​​}
  
  
set aWidth to (aSize’s width) * imgScale
  
set aHeight to (aSize’s height) * imgScale
  
  
set aRep to NSBitmapImageRep’s alloc()’s initWithBitmapDataPlanes:(missing value) pixelsWide:aWidth pixelsHigh:aHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:true isPlanar:false colorSpaceName:(NSCalibratedRGBColorSpace) bytesPerRow:0 bitsPerPixel:0
  
  
set newSize to {width:aWidth, height:aHeight}
  
aRep’s setSize:newSize
  
  
NSGraphicsContext’s saveGraphicsState()
  
NSGraphicsContext’s setCurrentContext:(NSGraphicsContext’s graphicsContextWithBitmapImageRep:aRep)
  
  
aSourceImg’s drawInRect:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) fromRect:(NSZeroRect) operation:(NSCompositeCopy) fraction:(1.0)
  
  
NSGraphicsContext’s restoreGraphicsState()
  
  
set newImg to NSImage’s alloc()’s initWithSize:newSize
  
newImg’s addRepresentation:aRep
  
  
return newImg
end resizedImage:toScale:

★Click Here to Open This Script 

Posted in file Image | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSBitmapImageRep NSImage NSPredicate NSScreen NSString NSURL NSUUID | Leave a comment

Keynoteで指定の表をX LabelとY Labelの交差するセルにデータを設定する v2

Posted on 12月 22, 2018 by Takaaki Naganoya

オープン中のKeynote書類のうち、指定タイトルのスライド中の表の値を取得したり設定するAppleScriptです。

シート名(タイトル)、表Yラベル、表Xラベル

の3つのパラメータをもとに、Keynote書類中の指定スライドの表の中のセルにアクセスして、値を取得/設定します。

処理対象のKeynote書類はKeynoteでオープンしている必要があり、各スライド中の表は1つのみ存在していることを処理の前提条件としています。

一応、再利用性を高めるためにScriptオブジェクト化しています。これがベストな方法なわけではありませんが、こんなもんでしょう。

AppleScript名:Keynoteで指定の表をX LabelとY Labelの交差するセルに設定する v2
— Created 2018-12-20 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set targCellNameX to "Col2"
set targCellNameY to "Row4"
set storeVal to "9999"

set keynoteSlide to "Page 2 Title"

set keynoteSlideObj to getTitleStringFromFrontKeynoteDocumentFilterByString(keynoteSlide) of getKeynoteSlideKit
–set aRes to getValueOnKeynoteTableByXYLabels(targCellNameY, targCellNameX, keynoteSlideObj) of getKeynoteSlideKit
setValueOnKeynoteTableByXYLabels(targCellNameY, targCellNameX, storeVal, keynoteSlideObj) of getKeynoteSlideKit

script getKeynoteSlideKit
  –Keynote タイトル指定してslideを取得する
  
on getTitleStringFromFrontKeynoteDocumentFilterByString(targString)
    set repTargList to {string id 10, string id 11, string id 13} –LF,CR,VTab一括置換
    
    
tell application "Keynote"
      if (count every document) = 0 then return
      
      
tell front document
        set tList to object text of default title item of every slide
      end tell
    end tell
    
    
–タイトルごとにゴミ取り(改行文字の削除)
    
set outList to {}
    
set sCount to 1
    
set hitF to false
    
repeat with i in tList
      set j1 to contents of i
      
set jTmp to (paragraphs of j1) as string –しつこい改行文字(?)を除去するための処理
      
      
set j2 to replaceTextMultiple(jTmp, repTargList, "") of me as string
      
if j2 is equal to targString then
        set hitF to true
        
exit repeat
      end if
      
      
set sCount to sCount + 1
    end repeat
    
    
if hitF = false then return false
    
    
tell application "Keynote"
      tell front document
        return item sCount of (every slide)
      end tell
    end tell
    
  end getTitleStringFromFrontKeynoteDocumentFilterByString
  
  
  
–リストを指定デリミタで区切ったテキストに変換
  
on listToStringUsingTextItemDelimiter(sourceList as list, textItemDelimiter as string)
    set anArray to current application’s NSArray’s arrayWithArray:sourceList
    
set aString to anArray’s componentsJoinedByString:textItemDelimiter
    
return (aString as string)
  end listToStringUsingTextItemDelimiter
  
  
  
–任意のデータから特定の文字列を複数パターン一括置換
  
on replaceTextMultiple(origData as string, origTexts as list, repText as string)
    set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to origTexts
    
set origData to text items of origData
    
set AppleScript’s text item delimiters to {repText}
    
set origData to origData as text
    
set AppleScript’s text item delimiters to curDelim
    
return origData
  end replaceTextMultiple
  
  
–行ラベルと列ラベルを個別に指定し、交点のセルに指定の値を入れる
  
on setValueOnKeynoteTableByXYLabels(targCellNameY, targCellNameX, storeVal, keynoteSlideObj)
    tell application "Keynote"
      tell keynoteSlideObj
        tell table 1
          
          
tell row 1
            set cList to value of every cell
            
set xRes to getOffsetFromList(targCellNameX, cList) of me
          end tell
          
          
tell column 1
            set dList to value of every cell
            
set yRes to getOffsetFromList(targCellNameY, dList) of me
          end tell
          
          
tell row yRes
            tell cell xRes
              set its value to storeVal
            end tell
          end tell
          
        end tell
      end tell
    end tell
  end setValueOnKeynoteTableByXYLabels
  
  
  
–行ラベルと列ラベルを個別に指定し、交点のセルに指定の値を入れる
  
on getValueOnKeynoteTableByXYLabels(targCellNameY, targCellNameX, keynoteSlideObj)
    tell application "Keynote"
      tell keynoteSlideObj
        tell table 1
          
          
tell row 1
            set cList to value of every cell
            
set xRes to getOffsetFromList(targCellNameX, cList) of me
          end tell
          
          
tell column 1
            set dList to value of every cell
            
set yRes to getOffsetFromList(targCellNameY, dList) of me
          end tell
          
          
tell row yRes
            tell cell xRes
              set tmpFormat to format
              
set its format to text
              
set aRes to its value
              
set its format to tmpFormat
              
              
if aRes = missing value then
                return ""
              else
                return aRes
              end if
            end tell
          end tell
          
        end tell
      end tell
    end tell
  end getValueOnKeynoteTableByXYLabels
  
  
  
on getOffsetFromList(aTarg, aList)
    using terms from scripting additions
      set aRes to offset of aTarg in aList
    end using terms from
    
return aRes
  end getOffsetFromList
  
  
  
  
—-Offset command overwrapper
  
on offset of bArg in anArg
    set aClass to class of anArg
    
set bClass to class of bArg
    
    
if {aClass, bClass} = {text, text} then –case 1
      return getOffset(anArg, bArg) of me
    else if {aClass, bClass} = {list, list} then –case 2 (The target case)
      return execOffsetList(bArg, anArg) of me
    else if {aClass, bClass} = {text, list} then –case 3 (Illegular case)
      return execOffsetList(bArg, {anArg}) of me
    else if {aClass, bClass} = {list, text} then –case 4 (Illegular case)
      return execOffsetList({bArg}, anArg) of me
    end if
  end offset
  
  
  
–1D List同士のoffset演算を行うルーチンの本体
  
on execOffsetList(aList as list, bList as list)
    set resList to {}
    
repeat with i in aList
      set j to contents of i
      
set aCount to 1
      
      
repeat with ii in bList
        set jj to contents of ii
        
if jj = j then
          set the end of resList to aCount
          
exit repeat
        end if
        
set aCount to aCount + 1
      end repeat
    end repeat
    
    
–見つかったItem No.が連続値かどうかチェック
    
set sRes to chkSequential(resList) of me
    
if sRes = true then
      return contents of first item of resList
    else
      return false
    end if
  end execOffsetList
  
  
  
–与えられた1D Listが連続値かどうかをチェックする
  
on chkSequential(aList)
    if length of aList = 1 then return true
    
if aList = {} then return false
    
    
set aFirst to first item of aList
    
set aList to rest of aList
    
    
repeat with i in aList
      set j to contents of i
      
if j is not equal to (aFirst + 1) then
        return false
      end if
      
copy j to aFirst
    end repeat
    
    
return true
  end chkSequential
  
  
  
–テキスト同士のoffset ofを(2.5x fasterで)実行する
  
on getOffset(str, searchStr)
    set d to divideBy(str, searchStr)
    
if (count d) is less than 2 then return 0
    
return (length of item 1 of d) + 1
  end getOffset
  
  
on divideBy(str, separator)
    set delSave to AppleScript’s text item delimiters
    
set the AppleScript’s text item delimiters to separator
    
set strItems to every text item of str
    
set the AppleScript’s text item delimiters to delSave
    
return strItems
  end divideBy
  
end script

★Click Here to Open This Script 

Posted in list Text | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Keynote NSArray | Leave a comment

Numbersで選択中のデータを2D Listで取得し、ユニーク化

Posted on 12月 3, 2018 by Takaaki Naganoya

Numbersの最前面の書類の選択中のシートの選択中のセルの値を2D List(Array)で取り出して、ユニーク化するAppleScriptです。

上記のような状態で本AppleScriptを実行すると、

{{"iMac4,1", "Yonah"}, {"iMac4,2", "Yonah"}, {"iMac5,2", "Merom"}, {"iMac5,1", "Merom"}, {"iMac6,1", "Merom"}, {"iMac7,1", "Merom"}, {"iMac8,1", "Penryn"}, {"iMac9,1", "Penryn"}, {"iMac10,1", "Wolfdale"}, {"iMac11,1", "Wolfdale, Lynnfield"}......} 

のような2D Listで結果が得られます。Numbersの選択中のセルからそのまま情報を取得すると1D Listとして結果が返ってきますが、別途selection rangeの情報を取得し、それをもとに2D Listに変換。得られた2D Listに対して重複データの除去をCocoaの機能を用いて行なっています。

特定のフィールド上の選択中のデータを1D Listとして取得し、重複データを検出するようなScriptは日常的に使っていますが、行データをすべて取得して2D Listとして取り出し、重複データ部分を除去するようなものは使っていませんでした。

AppleScript名:Numbersで選択中のデータを2D Listで取得し、ユニーク化
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aList to get2DListFromNumbersSelection() of me
if aList = "" then return "No Selection"

set bList to uniquifyList(aList) of me

on get2DListFromNumbersSelection()
  –Numbersで選択範囲を縦に区切ったリストを返す
  
tell application "Numbers"
    tell front document
      tell active sheet
        try
          set theTable to first table whose class of selection range is range
        on error
          return "" –何も選択されてなかった場合
        end try
        
        
tell theTable
          set selList to value of every cell of selection range –選択範囲のデータを取得
          
          
set selName to name of selection range –選択範囲のrange情報を取得
          
set {s1, s2} to parseByDelim(selName, ":") of me
          
          
–始点の情報を取得する
          
set s1Row to (address of row of range s1) as integer
          
set s1Column to (address of column of range s1) as integer
          
          
–終点の情報を取得する
          
set s2Row to (address of row of range s2) as integer
          
set s2Column to (address of column of range s2) as integer
          
          
–選択範囲の情報を取得する
          
set selHeight to s2Row – s1Row + 1 –高さ(Height of selection range)
          
set selWidth to s2Column – s1Column + 1 –幅(Width of selection range)
          
        end tell
      end tell
    end tell
  end tell
  
  
set aLen to length of selList
  
set aaLen to selHeight
  
  
set bList to {}
  
repeat with i from 1 to aaLen
    set aHoriList to {}
    
    
repeat with ii from 1 to selWidth
      set j1 to ii + (i – 1) * selWidth
      
set tmpCon to contents of item j1 of selList
      
      
set aClass to class of tmpCon
      
if aClass = number or aClass = integer or aClass = real then
        set tmpCon to tmpCon as integer
      end if
      
      
set the end of aHoriList to tmpCon
    end repeat
    
    
set the end of bList to aHoriList
  end repeat
  
  
return bList
end get2DListFromNumbersSelection

–テキストを指定デリミタでリスト化
on parseByDelim(aData, aDelim)
  set aText to current application’s NSString’s stringWithString:aData
  
set aList to aText’s componentsSeparatedByString:aDelim
  
return aList as list
end parseByDelim

–1D/2D Listのユニーク化
on uniquifyList(aList as list)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
return bArray as list
end uniquifyList

★Click Here to Open This Script 

Posted in list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSString Numbers | Leave a comment

機械学習で学習したMSの画像を連邦・ジオン軍判定してフォルダ分け v2

Posted on 11月 20, 2018 by Takaaki Naganoya

Create MLでガンダムのMS画像(連邦、ジオン区分け)を学習したCore ML Modelをもとに、画像をどちらの軍所属機体かを判定してフォルダ分けするAppleScriptです。

機械学習(深層学習)データをもとに判定を行って対話型処理ではなくバッチ処理的なフローでデータを「なんとなく」推論して区分けを行うサンプルです。非力なIOT機器が機械学習データをもとに推論処理できるぐらいなので、機械学習データを使ってAppleScriptで推論処理できないはずがありません。推論処理時間もさほどかからず、機械学習データを一括処理的なワークフローの中で利用することが可能です。

例によって「戦場の絆」の公式ページから落としてきたMSの画像を連邦軍とジオン軍、および両軍の鹵獲機体の4フォルダに分けてCreate MLで学習させてみました。

実行するとMS画像が入っているフォルダ(各自ご用意ください)と、連邦軍機体(efsf)およびジオン軍機体(zeon)を仕分けるフォルダを聞かれます。あとはプログラム側がなんとなく推論して仕分けを行います。

前回掲載のモデルもそうですが、きちんと学習後にテストデータで評価を行っています。だいたい8割ぐらいのヒット率ではあるものの、特定の画像については間違えることがあらかじめわかっています。

実行にあたっては、macOS 10.14上でCoreML Modelを組み込んだフレームワーク「msDetector.framework」をインストールして、本ScriptをScript Debugger上で動作させてください。

–> Download msDetector.framework (To ~/Library/Frameworks/)

–> Download msDetector (Code Signed executable AppleScript applet with Framework and libraries in its bundle)

Core MLで判定時に詳細なデータを出力させ、その可能性が0.8を上回るものだけを処理するようにしてみました。ただ、全力で間違った判定を行うケースもあるため、単なる「気休め」です。

テストデータでは、連邦軍側が72機体中間違いが9個(正解87.5%)、ジオン軍側が57機体中間違いが3個(正解95%)。鹵獲機体のデータを排除すると、もう少しいい値が出るかもしれません。ただ、学習させたデータとほぼ同じデータ(数が少ない)で再度判定を行っているだけなので、このぐらいの確度が出ないと逆に困ります。

また、確度が高くないものは排除するように処理したので、確度が低い機体の画像バリエーションを増やして学習画像数を足すと正解の確率が上がるのではないか、などと「なんとなく」考えています。

Create MLでは、もっときちんとした表データやタグ付き自然言語テキストデータ(日本語だと形態素への区分けはあらかじめやっておく必要アリ?)の学習が行えるようなので(自然言語テキストの学習例は英語のものしか見かけないけれども)いろいろやっておきたいところです。

今回は「アニメとかゲームに出てくるMSの画像」の判定という、実用性をまったく考えないで行なってみたものですが、自分の身の回りで有用性の高そうな処理といえば……アイコンやプレゼン用資料の作成画像を学習させておおまかなジャンル分けをしておき(メール系のアイコンとか)、未知のアイコン素材をダウンロードしてきたら機械学習データをもとに自動でフォルダ分けするとかでしょうか。

さすがにアイコン画像だとデフォルメされすぎていて、MicrosoftのAzure Computer Vision APIとかでも画像認識してくれなさそうなので。

AppleScript名:機械学習で学習したMSの画像の連邦軍、ジオン軍判定してフォルダ分け v2.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/11/20
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7" — Mojave (10.14) or later & Script Debugger
use framework "Foundation"
use framework "AppKit"
use framework "msDetector"
use scripting additions

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 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 NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey

set aFol to POSIX path of (choose folder with prompt "Choose MS Image folder to go!")
set efsfFol to POSIX path of (choose folder with prompt "EFSF folder")
set zeonFol to POSIX path of (choose folder with prompt "Zeon folder")

–指定フォルダ以下のファイルをすべて取得(再帰ですべて取得)
set aSel to retFullpathsWithinAFolderWithRecursive(aFol) of me

–取得したファイルリストを指定UTIでフィルタリング
set filRes2 to filterPOSIXpathListByUTI(aSel, "public.image") of me

–機械学習モデルを用いて区分けを行い、任意の画像の連邦軍(efsf)、ジオン軍(zeon)へのフォルダ分け(コピー)
set msClassifier to current application’s msClassify’s alloc()’s init()

repeat with i in filRes2
  set aFile to contents of i
  
set aImage to (current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile)
  
  
set resDict to (msClassifier’s ImageClassifierWithImageAndRetDict:aImage) –returns NSDictionary
  
  
–Dictinaryのvalueをソートして、最も値の大きいvalueのkeyを求める
  
set sortedArray to (current application’s NSMutableArray’s arrayWithArray:(resDict’s allValues()))
  (
sortedArray’s sortUsingSelector:"compare:")
  
set aRes to (last item of (sortedArray as list)) –末尾のアイテムが一番値の大きい数値
  
set resKey to first item of ((resDict’s allKeysForObject:aRes) as list) –最大値をもとにKeyを求める
  
  
–可能性の高いものだけ処理してみる(自信たっぷりに全力で間違えることもけっこうある)
  
if (aRes as real) > 0.8 then
    if resKey begins with "efsf" then
      –efsf & efsf_stolen
      
set aRes to (my copyFileAt:aFile toFolder:efsfFol)
    else
      –zeon & zeon_stolen
      
set aRes to (my copyFileAt:aFile toFolder:zeonFol)
    end if
  end if
end repeat

–POSIX path listから指定UTIに含まれるものをPOSIX pathのリストで返す
on filterPOSIXpathListByUTI(aList, targUTI)
  set newList to {}
  
repeat with i in aList
    set j to contents of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, targUTI)
    
if utiRes is not equal to {} then
      set the end of newList to j
    end if
  end repeat
  
return newList
end filterPOSIXpathListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

–指定フォルダ以下のすべてのファイルを再帰で取得
on retFullpathsWithinAFolderWithRecursive(aFol)
  set anArray to NSMutableArray’s array()
  
set aPath to NSString’s stringWithString:aFol
  
set dirEnum to NSFileManager’s defaultManager()’s enumeratorAtPath:aPath
  
  
repeat
    set aName to (dirEnum’s nextObject())
    
if aName = missing value then exit repeat
    
set aFullPath to aPath’s stringByAppendingPathComponent:aName
    
anArray’s addObject:(aFullPath as string)
  end repeat
  
  
return anArray as list
end retFullpathsWithinAFolderWithRecursive

on copyFileAt:POSIXPath toFolder:folderPath
  set POSIXPath to NSString’s stringWithString:POSIXPath
  
set folderPOSIXPath to NSString’s stringWithString:folderPath
  
set theName to POSIXPath’s lastPathComponent()
  
set newPath to folderPOSIXPath’s stringByAppendingPathComponent:theName
  
set fileManager to NSFileManager’s defaultManager()
  
set theResult to fileManager’s copyItemAtPath:POSIXPath toPath:newPath |error|:(missing value)
  
return (theResult as integer = 1)
end copyFileAt:toFolder:

★Click Here to Open This Script 

Posted in file File path Image list Machine Learning Record | Tagged 10.14savvy NSArray NSFileManager NSMutableArray NSPredicate NSSortDescriptor NSString NSURL NSURLTypeIdentifierKey | 1 Comment

Post navigation

  • Older posts
  • Newer posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • CotEditorで2つの書類の行単位での差分検出
  • macOS 15, Sequoia
  • 指定のWordファイルをPDFに書き出す
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Numbersで選択範囲のセルの前後の空白を削除
  • メキシカンハットの描画
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • AdobeがInDesign v19.4からPOSIX pathを採用
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • AppleScriptによる並列処理
  • Cocoa Scripting Course 続刊計画
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • NaturalLanguage.frameworkでNLEmbeddingの処理が可能な言語をチェック
  • Keynote/Pagesで選択中の表カラムの幅を均等割
  • Keynote、Pages、Numbers Ver.14.0が登場
  • macOS 15 リモートApple Eventsにバグ?
  • デフォルトインストールされたフォント名を取得するAppleScript
  • AppleScript入門① AppleScriptってなんだろう?

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (194) 14.0savvy (146) 15.0savvy (128) CotEditor (66) Finder (51) iTunes (19) Keynote (116) NSAlert (61) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (53) NSDictionary (28) NSFileManager (23) NSFont (21) NSImage (41) NSJSONSerialization (21) NSMutableArray (63) NSMutableDictionary (22) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (119) NSURL (98) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (76) Pages (54) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • check sum
  • Clipboard
  • Cocoa-AppleScript Applet
  • Code Sign
  • Color
  • Custom Class
  • date
  • dialog
  • diff
  • drive
  • Droplet
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC