Archive for the '10.11 savvy' Category

2017/12/10 クリップボードに入ったproperty宣言部分を文字列長と文字コードでソート

クリップボードに入ったAppleScriptのproperty宣言部分を文字列長と文字コードでソートしてクリップボードに入れるAppleScriptです。

最近、AppleScript中でCocoaの機能を呼び出す処理を書く際に、プログラムリストが(横に)長くなることを避けるため、冒頭部分にpropertyで各Cocoa Objectへの参照を宣言するようにしていますが、、、、

正直、ここの宣言部分が乱雑なので、清書するために書いたものです。スクリプトメニューに入れて利用することを前提としています。

prop_before.png

property宣言部分をコピーして本Scriptを実行。property宣言部分の文字数が少ない順&文字コード順にソートを行います。

prop_after.png

結果をペースト。より、宣言部分が多いと(見た目の)効果が大きいです。

after2.png

対象がスクリプトエディタだけであれば、スクリプトエディタ自体をAppleScriptからコントロールして、冒頭のproperty宣言部分を抽出してproperty宣言文の構文要素をピックアップし、クリップボードを経由しないで書き換えることも可能です。

ただし、スクリプトエディタ以外の記述ソフト(Script Debugger、ASObjC Explorer 4、Xcode)を利用しているケースが多いことを考慮してクリップボード経由でデータのやり取りを行わせてみました。

AppleScript書類に対してのアクセスについては、正直Script Debuggerよりもスクリプトエディタの方が上(リッチテキスト形式でもアクセスできるので、構文色分けを利用して各種構文要素にアクセス可能)。スクリプト内容を解析して書き換えを行うような用途については、「Piyomaru Script Assistant」(電子書籍購入者の方への購入特典)をご利用いただいている方にはそのパワーをご理解いただけると思います。

同ツールはAppleScriptでAppleScriptを解析して、AppleScript自体を書き換えるというコンテクストメニューからのAppleScript呼び出しを超強化したものです。

AppleScript名:クリップボードに入ったproperty宣言部分を文字列長と文字コードでソート
– Created 2017-12-08 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/5014

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSMutableArray : a reference to current application’s NSMutableArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor

set aStr to (the clipboard)
if aStr = “” then
  display dialog “No Data in Clipboard”
  
return
end if

set aList to paragraphs of aStr
set bList to {}
repeat with i in aList
  set j to contents of i
  
if j ≠ {} then
    set jList to words of j
    
if jList ≠ {} then
      if contents of first item of jList = “property” then
        set curLabel to contents of second item of jList
        
set the end of bList to {aLabel:curLabel, aCon:j}
      end if
    end if
  end if
end repeat

if bList = {} then
  display dialog “Error”
  
return
end if

–昇順ソート
set cList to sort1DListByIndicatedStringLength(bList, “aLabel”, true) of me

–ソートしたlist of recordからaCon(元のproperty宣言行そのもの)を一括で取り出す
set dArray to (NSMutableArray’s arrayWithArray:cList)’s valueForKeyPath:“aCon”

–listをテキストに
set dStr to retStrFromArrayWithDelimiter(dArray, return) of me

set the clipboard to dStr

–1D Listを文字列長でソート v2
on sort1DListByIndicatedStringLength(aList as list, aSortKey as string, sortOrder as boolean)
  set aArray to NSArray’s arrayWithArray:aList
  
set descLabel1 to NSString’s stringWithString:(aSortKey & “.length”)
  
set descLabel2 to NSString’s stringWithString:aSortKey
  
set desc1 to NSSortDescriptor’s sortDescriptorWithKey:descLabel1 ascending:sortOrder
  
set desc2 to NSSortDescriptor’s sortDescriptorWithKey:descLabel2 ascending:true selector:“localizedCaseInsensitiveCompare:”
  
set bArray to aArray’s sortedArrayUsingDescriptors:{desc1, desc2}
  
return bArray as list
end sort1DListByIndicatedStringLength

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

★Click Here to Open This Script 

2017/12/08 指定文字の花文字を取得してRTFで書き出す

指定文字の花文字を取得して、収録グリフ数が10,000以上のフォントから30をデスクトップにRTFで書き出すAppleScriptです。

hanamoji1.png

前のバージョンからの改良点は、

 〇慊衒源が指定フォント中にグリフを持っているかどうかチェックしてから処理
 ▲ぅ鵐好函璽襪気譴討い襯侫ント数が少ない場合への対処

といったところです。

花文字作成部分の処理内容は、

 ”漸荵慊衒源を収録しているフォントの一覧を取得
 ↓,納萋世靴織侫ントのうち、グリフ数が10,000以上のものをピックアップ
 スタイル付きテキストを作成。仕上がり(描画)サイズを取得
 せ転紊りサイズでRaw画像を作成
 ズ鄒したRaw画像を塗りつぶす(White)
 ε匹蠅弔屬靴Raw画像にスタイル付きテキストを描画
 Raw画像の各座標から色情報をピックアップ、指定スレッショルド値以上であればドットが存在していると判定
 ┘疋奪箸存在している場合には描画文字列を、存在していない場合にはスペースを配列に追加
 作成した2次元配列をテキストに変換。フォント名をテキストに含める指定を行なっている場合にはフォントのPostScript名を文字列に出力

というところです。

これまでは(macOS 10.10より前)AppleScriptで画像処理を行おうとすると、Photoshopあたりで処理するのが定番でしたが、Cocoaの機能を利用することで、カラープロファイル処理が厳密に求められるような内容でなければPhotoshopなしでけっこうな処理が行えるようになってきました。

テキスト処理において(文字コード自動判別が可能になったため)テキストエディタが要らなくなってきたように、データベース処理で(小規模データであれば)FileMaker Proが要らなくなってきて、同様に画像処理でもPhotoshopなしで処理できるものが増えてきた今日このごろです。

AppleScript名:指定文字の花文字を取得してRTFで書き出す
– Created 2017-11-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/5012

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

set aString to “あ”
set hanaMax to 30
set hanaSize to 36
set targFontName to “Osaka-Mono” –”Courier New”–結果を出力するRTFのフォント名(PostScript名)

set fRes to getEveryFontPSNameANdGlyphsNum() of me
set theArray to NSArray’s arrayWithArray:fRes
set thePred to NSPredicate’s predicateWithFormat:“fontNum > 10000″
set bArray to (theArray’s filteredArrayUsingPredicate:thePred) as list

if hanaMax > (length of bArray) then
  set hanaMax to (length of bArray)
end if

set aCount to 1

repeat hanaMax * 2 times
  set aFontName to contents of item aCount of bArray
  
  
–花文字文字列を作成
  
set fRes to getHanamojiStr(hanaSize, fontName of aFontName, aString, 0.7, true) of me
  
if fResfalse then
    –StyledStringで結果出力(RTFとしてファイル保存)
    
set aStyledStr to makeRTFfromParameters(fRes, targFontName, 11, 0.0, 0.0) of me
    
set aRange to current application’s NSMakeRange(0, aStyledStr’s |length|())
    
set aVal1 to NSFont’s fontWithName:targFontName |size|:11
    
    
aStyledStr’s beginEditing()
    (
aStyledStr’s addAttribute:(NSFontAttributeName) value:aVal1 range:aRange)
    
aStyledStr’s endEditing()
    
    
set targFol to POSIX path of (path to desktop)
    
set aUUID to NSUUID’s UUID()’s UUIDString() as text
    
set bRes to my saveStyledTextAsRTF(aUUID, targFol, aStyledStr) –RTFで書き出す
    
    
set aCount to aCount + 1
    
if aCount > hanaMax then exit repeat
  end if
end repeat

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

–指定Raw画像中の指定座標のピクセルの色をRGBで取り出す
on getColorFromRawImage(aRawimg, x as real, y as real)
  set origColor to (aRawimg’s colorAtX:x y:y)
  
set srgbColSpace to NSColorSpace’s deviceRGBColorSpace
  
if srgbColSpace = missing value then return false
  
  
set aColor to (origColor’s colorUsingColorSpace:srgbColSpace)
  
  
set aRed to (aColor’s redComponent()) * 255
  
set aGreen to (aColor’s greenComponent()) * 255
  
set aBlue to (aColor’s blueComponent()) * 255
  
  
return {aRed as integer, aGreen as integer, aBlue as integer}
end getColorFromRawImage

–画像のうえに指定のスタイル付きテキストを描画して画像を返す
on drawAttributedStringsOnImage(anImage, anAssrStr, xPos 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))
  
  
anImage’s lockFocus()
  
set theRect to {{x:0, y:0}, {width:aWidth, height:aHeight}}
  
set theNSBezierPath to NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  
fillColor’s |set|()
  
theNSBezierPath’s fill()
  
anImage’s unlockFocus()
  
  
return anImage
end makeImageWithFilledColor

–2D Listをアイテム間および行間のデリミタを個別に指定してテキスト変換
on list2dToStringByUsingDelimiters(aList 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()
  set aFontList to NSFontManager’s sharedFontManager()’s availableFonts()
  
set thePred to NSPredicate’s predicateWithFormat:“NOT SELF BEGINSWITH ’.’”
  
set aFontList to (aFontList’s filteredArrayUsingPredicate:thePred) as list
  
  
set aList to {}
  
repeat with i in aFontList
    set aName to contents of i
    
set aNum to countNumberOfGlyphsInFont(aName) of me
    
set the end of aList to {fontName:aName, fontNum:aNum}
  end repeat
  
  
return aList
end getEveryFontPSNameANdGlyphsNum

–指定Postscript名称のフォントに定義されている文字数を数えて返す
on countNumberOfGlyphsInFont(fontName 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

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsRTF(aFileName as string, targFol as string, aStyledString)
  set bstyledLength to aStyledString’s |string|()’s |length|()
  
set bDict to NSDictionary’s dictionaryWithObject:“NSRTFTextDocumentType” forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict
  
  
set theName to NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“rtf”
  
  
return (bRTF’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsRTF

–指定名称のフォントに指定の文字コードが含まれているかチェック
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

★Click Here to Open This Script 

2017/12/06 指定フォルダ以下のテキストファイルのファイル名のうち連番部分を抽出して、欠落や重複を検出する

指定フォルダ以下の指定形式のファイルのファイル名をSpotlightですべて取得し、重複や連番部分の欠落を抽出するAppleScriptです。

しょっちゅう同じようなScriptを作っているような気もしますが、それだけ個人的な必要度が高いものかもしれません。

連番部分を含む命名規則を持つファイル群、

  1AXXXXXX-101.txt
  1AXXXXXX-72.txt
  1AXXXXXX-9.txt

から(ファイル名はサンプル)、ファイル名の共通部分をスキャンして、ファイルごとに変更になる部分を抽出しています。一応、ねんのために拡張子を外したデータを処理しています。

ファイル名の共通部分をデータ同士から抽出する、というのが本Scriptの新機軸です。

データすべてから変更部分を取得するのではなく、データの文字数の最大、最小を取得し、それぞれの文字列に該当するデータを10件ずつピックアップし、その間で変更部分の検出を行なっています。

こうした「固定部分の自動検出」というのは、仕様がきちんと存在していない仕事において必要になってくると思われます。すべてのファイル名データから命名ルールを自動検出したり、例外に該当するものを除外したり、ということも考えられそうです。

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

AppleScript名:指定フォルダ以下のテキストファイルのファイル名のうち連番部分を抽出して、欠落や重複を検出する
– Created 2017-12-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use mdLib : script “Metadata Lib” version “1.0.0″
use bPlus : script “BridgePlus”
–http://piyocast.com/as/archives/5011

property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property SMSForder : a reference to current application’s SMSForder
property NSMutableSet : a reference to current application’s NSMutableSet
property NSIndexSet : a reference to current application’s NSIndexSet

set origFol to (POSIX path of (choose folder))
set txtFiles to mdLib’s searchFolders:{origFol} searchString:“kMDItemContentType == %@” searchArgs:{“public.plain-text”}

–ファイル名部分から拡張子を削除して収集
set anArray to ((current application’s NSMutableArray’s arrayWithArray:txtFiles)’s valueForKeyPath:“lastPathComponent.stringByDeletingPathExtension”)

–ファイル名文字列リストから、共通部分(変更のない部分)を抽出
set intStr to retInterSectionStr(anArray) of me

set cList to {}
repeat with i in (anArray as list)
  set aRes to repChar(i as string, intStr, “”) of me
  
set aNumF to chkNumeric(aRes) of me
  
if aNumF = true then
    set the end of cList to aRes as integer
  else
    log i
  end if
end repeat

set aRes to calcGaps(cList) of me
set bRes to returnDuplicatesOnly(cList) of me

return {gaps:aRes, dups:bRes}
–>  {gaps:{195, 284, 285}, dups:{}}

–与えられた文字列リストのうち、文字列共通部分を抽出
on retInterSectionStr(anArray)
  set aMin to (anArray’s valueForKeyPath:“@min.length”) as integer
  
set aMax to (anArray’s valueForKeyPath:“@max.length”) as integer
  
  
set bList to {}
  
repeat with i from aMin to aMax
    set aCount to 0
    
    
repeat with ii in (anArray as list)
      set aLen to length of ii
      
if aLen = i then
        set the end of bList to contents of ii
        
if aCount = 10 then
          exit repeat
        else
          set aCount to aCount + 1
        end if
      end if
    end repeat
    
  end repeat
  
  
set aStr to contents of first item of bList
  
set bList to rest of bList
  
  
repeat with i in bList
    set bStr to contents of i
    
set intStr to calcIntersection(aStr, bStr) of me
    
if intStr = false then
      exit repeat
    else
      copy intStr to aStr
    end if
  end repeat
  
  
return aStr
end retInterSectionStr

–2つの文字列から共通部分を(先頭から)抽出
on calcIntersection(aStr, bStr)
  set aList to characters of aStr
  
set bList to characters of bStr
  
  
set aLen to length of aList
  
set bLen to length of bList
  
  
if aLen bLen then
    set aMax to bLen
  else
    set aMax to aLen
  end if
  
  
set hitF to false
  
repeat with i from 1 to aMax
    set aChar to contents of item i of aList
    
set bChar to contents of item i of bList
    
if aChar is not equal to bChar then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
  
if hitF = false then return false
  
  
return (text 1 thru (i - 1) of aStr)
end calcIntersection

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

–文字列が数字のみから構成されているかを調べて返す
on chkNumeric(checkString as string)
  set digitCharSet to current application’s NSCharacterSet’s characterSetWithCharactersInString:“0123456789″
  
set ret to my chkCompareString:checkString baseString:digitCharSet
  
return ret as boolean
end chkNumeric

on chkCompareString:checkString baseString:baseString
  set aScanner to current application’s NSScanner’s localizedScannerWithString:checkString
  
aScanner’s setCharactersToBeSkipped:(missing value)
  
aScanner’s scanCharactersFromSet:baseString intoString:(missing value)
  
return (aScanner’s isAtEnd()) as boolean
end chkCompareString:baseString:

–連番リストからGap検出
on calcGaps(aList as list)
  set nArray to (NSMutableArray’s arrayWithArray:aList)
  
set maxRes to (nArray’s valueForKeyPath:“@max.self”)’s intValue()
  
set minRes to (nArray’s valueForKeyPath:“@min.self”)’s intValue()
  
  
–最小値から最大値までの連番リスト作成
  
set theIndexSet to NSIndexSet’s indexSetWithIndexesInRange:{minRes, maxRes}
  
set theList to (SMSForder’s arrayWithIndexSet:theIndexSet) as list
  
  
–補集合
  
set aSet to NSMutableSet’s setWithArray:theList
  
set bSet to NSMutableSet’s setWithArray:nArray
  
aSet’s minusSet:bSet
  
  
return aSet’s allObjects() as list
end calcGaps

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

★Click Here to Open This Script 

2017/12/05 指定フォルダ内のラベル(Yellow, Red, Orange)のファイル一覧を取得

デスクトップフォルダ以下のファイルのうち、Spotlightで指定のラベル(複数のラベル)のファイルをリストアップするAppleScriptです。

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

本来、Spotlight検索時に指定するPredicate文では「IN」演算子(AppleScriptの「is in」とほぼ等価)が使えるので、当初書いていたような(↓)調子で書けるかと思っていたのですが、エラーが出て動きません。

Shaneにも相談してみたのですが、ORで複数条件を展開する方法しか通らないね、という話に。

であれば、文字列で決め打ちにするのではなく、Predicate文そのものをパラメータ(list)に合わせて生成するようにしてみました。

AppleScript名:指定フォルダ内のラベル(Yellow, Red, Orange)のファイル一覧を取得(未遂)
– Created 2017-09-23 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use mdLib : script “Metadata Lib” version “1.0.0″
–http://piyocast.com/as/archives/5010

set theFolder to path to desktop
set fRes to mdLib’s searchFolders:{theFolder} searchString:“kMDItemFSLabel IN %@” searchArgs:{{5, 6, 7}} –This script cause error
–> error “NSComparisonPredicate with type other than NSBetweenPredicateOperatorType and right expression which is an array given to NSMetadataQuery (kMDItemFSLabel IN {5, 6, 7})” number -10000

★Click Here to Open This Script 

AppleScript名:指定フォルダ内のラベル(Yellow, Red, Orange)のファイル一覧を取得
– Created 2017-12-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use mdLib : script “Metadata Lib” version “1.0.0″
–http://piyocast.com/as/archives/5010

set aLabelList to {5, 6, 7} –Yellow, Red, Orange
–0: No Label, 1: Gray, 2: Green, 3: Purple, 4: Blue, 5: Yellow, 6: Red, 7: Orange
set thePath to POSIX path of (path to desktop)
set aRes to spotlightFindByLabels(aLabelList, thePath) of me
–> list of POSIX path

on spotlightFindByLabels(aLabelList as list, thePath as string)
  set aList to makeRepeatinglList(length of aLabelList, “kMDItemFSLabel == %@”)
  
set aStr to retStrFromArrayWithDelimiter(aList, ” OR “) of me
  
set fRes to mdLib’s searchFolders:{thePath} searchString:aStr searchArgs:aLabelList
  
return fRes
end spotlightFindByLabels

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

–指定回数、指定アイテムを連結したリストを作成
on makeRepeatinglList(hitNum as integer, hitItem as string)
  set outList to {}
  
repeat hitNum times
    set the end of outList to hitItem
  end repeat
  
return outList
end makeRepeatinglList

★Click Here to Open This Script 

2017/12/04 自然言語で指定した日時以降に作成されたファイルをSpotlight検索

デスクトップフォルダ以下のファイルのうち、作成日付が自然言語で指定した日時以降のものをリストアップするAppleScriptです。

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

機能的にはとくに凝ったことをしていませんが、複数の機能を組み合わせることで柔軟な処理が行えるという見本です。ただし、OS側の自然言語ベースの日付特定処理がそれほど凝ったものではないので、ファイル検索を行う前にきちんと日時を取得できるかどうかのチェックが必要です。

AppleScript名:自然言語で指定した日時以降に作成されたファイルをSpotlight検索
– Created 2017-09-21 by Takaaki Naganoya
– Modified 2017-09-22 by Shane Stanley
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use mdLib : script “Metadata Lib” version “1.0.0″
–http://piyocast.com/as/archives/5009

set aDate to getDatesIn(“先週の月曜日”) of me –”last Monday” in Japanese
log aDate

set thePath to POSIX path of (path to desktop)

set theFiles to mdLib’s searchFolders:{thePath} searchString:(“kMDItemFSCreationDate >= %@”) searchArgs:{aDate}
–> returns POSIX path list

on getDatesIn(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
set theDetector to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeDate) |error|:(missing value)
  
set theMatch to theDetector’s firstMatchInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
if theMatch = missing value then error “No date found with String:” & aString
  
set theDate to theMatch’s |date|()
  
return theDate as date
end getDatesIn

★Click Here to Open This Script 

2017/12/02 デスクトップ上のRetina解像度のスクリーンショット画像でICNS以外のものを検索

デスクトップフォルダ以下のスクリーンショット画像ファイルのうち、Retina解像度(144dpi)の画像でICNS以外のものを検索するAppleScriptです。

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

さらに、スクリーンショット画像のうちフルスクリーンを対象にしたキャプチャなのか、部分的に選択されたエリアのキャプチャなのかを指定できます。また、カラープロファイルの指定もできるので、「複数接続されているディスプレイのうち、どのディスプレイ上で行われたキャプチャなのか」を特定することも可能です(すべてが同一モデルだと無理ですけれども)。

AppleScript名:デスクトップ上のRetina解像度のスクリーンショット画像でICNS以外のものを検索
– Created 2017-09-23 by Takaaki Naganoya
– Modified 2017-12-01 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use mdLib : script “Metadata Lib” version “1.0.0″
–http://piyocast.com/as/archives/5008

set theFolder to path to desktop
set fRes to mdLib’s searchFolders:{theFolder} searchString:“kMDItemResolutionHeightDPI == %@ && kMDItemResolutionWidthDPI == %@ && kMDItemIsScreenCapture == %@ && NOT (kMDItemContentTypeTree contains %@)” searchArgs:{144, 144, true, “com.apple.icns”}
–> {”/Users/me/Desktop/FromDesktop/スクリーンショット 2015-12-18 13.02.14.png”, “/Users/me/Desktop/FromDesktop/スクリーンショット 2015-08-14 18.08.41.png”}

★Click Here to Open This Script 

2017/12/01 デスクトップ上のRetina解像度の画像でICNS以外のものを検索

デスクトップフォルダ以下の画像ファイルのうち、Retina解像度の画像でICNS以外のものを検索するAppleScriptです。

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

AppleScript名:デスクトップ上のRetina解像度の画像でICNS以外のものを検索
– Created 2017-09-23 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use mdLib : script "Metadata Lib" version "1.0.0"
–http://piyocast.com/as/archives/5007

set theFolder to path to desktop
set fRes to mdLib’s searchFolders:{theFolder} searchString:"kMDItemResolutionHeightDPI == %@ && kMDItemResolutionWidthDPI == %@ && NOT (kMDItemContentTypeTree contains %@)" searchArgs:{144, 144, "com.apple.icns"}
–> {"/Users/me/Desktop/FromDesktop/スクリーンショット 2015-12-18 13.02.14.png", "/Users/me/Desktop/FromDesktop/conicon.png", "/Users/me/Desktop/FromDesktop/スクリーンショット 2015-08-14 18.08.41.png"}

★Click Here to Open This Script 

2017/12/01 Finderで選択中のフォルダ以下からPagesとMarkdown書類を抽出

Finder上で選択中のフォルダ(多分)の下からPages書類とMarkDown書類をすべてSpotlight検索するAppleScriptです。

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

AppleScript名:Finderで選択中のフォルダ以下からPagesとMarkdown書類を抽出
– Created 2017-10-28 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use mdLib : script “Metadata Lib” version “1.0.0″
–http://piyocast.com/as/archives/5006

tell application “Finder”
  set aSel to (selection as alias list)
  
if aSel = {} then return
  
set origPath to POSIX path of (first item of aSel)
end tell

set aResList to mdLib’s searchFolders:{origPath} searchString:(“kMDItemContentTypeTree CONTAINS %@ || kMDItemContentTypeTree CONTAINS %@”) searchArgs:{“net.daringfireball.markdown”, “com.apple.iwork.pages.sffpages”}

★Click Here to Open This Script 

2017/11/30 Keynoteで最前面の書類の全スライドにマスタースライドを再適用する

Keynoteでオープン中の最前面の書類の全スライドにマスタースライドを再適用するAppleScriptです。

書類中の各スライド(ページ)上のタイトルの大きさや位置が、作業中にバラバラになってしまった場合に、本Scriptを実行するとスライドを再適用するため、位置や大きさがそろいます。

本当は各スライド上のタイトルオブジェクト(default title item)の大きさや位置をループで揃えようとしてこの何倍かの長さのScriptを書いていたのですが、ふと「マスターを再適用すれば簡単にすぐ終わるのでは?」と思い、やってみたらあっという間でした。

確認はKeynote v7.3.1+macOS 10.12.6で行っています。macOS 10.13(「macOS Vista」と呼んでいる)は検証用のサブマシンには入れてありますが、あまりにバグが多くて出来が悪いので、メイン環境には入れないつもりです。

AppleScript名:Keynoteで最前面の書類の全スライドにマスタースライドを再適用する
–http://piyocast.com/as/archives/5005
tell application “Keynote”
  tell front document
    set sList to every slide
    
repeat with i in sList
      tell i
        set curSlide to base slide
        
set base slide to curSlide
      end tell
    end repeat
  end tell
end tell

★Click Here to Open This Script 

2017/11/29 指定のテキストからHTMLタグを除去(比較)

Shane Stanleyから「HTMLのタグ外す程度ならFrameworkまでビルドしなくてもいいんじゃね?」的な指摘&サンプルコード(NSXMLParser)があって、手元のさまざまなHTMLタグ外しルーチンを整理して検討してみました。

HTMLからのタグ外し(detag)については、いろいろやり方があって、それこそHTMLReaderみたいな「HTMLそのものを解釈する」フル装備のフレームワークもあれば、単にテキストとして処理するやり方までさまざまです。

昨日の件についていえば、「たまたま文字列中にHTMLタグのような文字列が入った場合に除去したい」というぐらいの用途でした。

また一方で、HTMLファイル全体を与えたときにどのような挙動になるのか、という評価軸もあります。

さらに、日本語文字列が入っていた場合にどうなるのか、文字化けせずに処理されるのかという話もあります。日本語圏で大丈夫ということは、だいたい同じぐらいの難易度の中国語、韓国語圏でも大丈夫であることが期待されます。アラビア語については未検証なのでわかりません。

そして、処理速度。0.00x秒ぐらいの処理時間でどちらが速いといっても、もはや時間が短すぎてよくわからないレベルに達しています。100回ループで回して時間計測すると、もはや誤差ぐらいの差でしかありません。

detag.png

評価結果を見てみると、

NSAttributedString:日本語が化けるし、<BR>タグが改行として解釈されるので、簡単ではあるものの使い勝手はいまひとつ

removeTagKit:高速で処理も安定している一方で、その程度でフレームワークを作ってインストールさせるのはどうか的な話があるのと、HTMLファイルを単体で与えたときに処理できませんでした

NSXMLParser:速くていいんだけど、<BR>とか<P>とかの単体で存在しているタグが混入しているとエラーが出たりした

NSScanner:HTMLを単なる文字列として処理するやり方がいちばん安定していた

といった、割と「身も蓋もない」話になってしまいました。

HTMLファイルを読み取ってテキスト抽出するのであれば、HTMLReader.frameworkを併用して文字コードを自動判別させてみるとよいでしょう。

AppleScript名:指定のテキストからHTMLタグを除去(NSXMLParser)
– Created 2017-11-29 by Shane Stanley
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
–http://piyocast.com/as/archives/5004

property textArray : missing value
property anError : missing value

set theString to "<a>repeat</a>〜end repeat"
my stripHTMLTagsIn:theString
–>  "repeat〜end repeat"

on stripHTMLTagsIn:theString
  set my textArray to current application’s NSMutableArray’s array()
  
set theString to current application’s NSString’s stringWithFormat_("<root>%@</root>", theString)
  
set theString to theString’s stringByReplacingOccurrencesOfString:"&" withString:"&amp;"
  
set theData to theString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set theParser to current application’s NSXMLParser’s alloc()’s initWithData:theData
  
theParser’s setDelegate:me
  
set theResult to theParser’s parse()
  
if not theResult then error (anError’s |description|() as text)
  
return (textArray’s componentsJoinedByString:"") as list of string or string –as anything
end stripHTMLTagsIn:

on parser:theParser foundCharacters:aString
  textArray’s addObject:aString
end parser:foundCharacters:

on parser:anNSXMLParser parseErrorOccurred:anNSError
  set my anError to anNSError
end parser:parseErrorOccurred:

★Click Here to Open This Script 

AppleScript名:指定のテキストからHTMLタグを除去(NSScanner)
– Created 2016-12-12 by Shane Stanley
– Modified 2016-12-14 by edama2
– Modified 2017-11-28 by Takaaki Naganoya
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/5004

–set aStr to read (choose file)
set aStr to "<a>repeat</a>〜end repeat<BR>"
set aRes to (trimStrFromTo(aStr, "<", ">") of me)
–>  "repeat〜end repeat"

on trimStrFromTo(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:(reference)
    
    
theScanner’s scanString:fromStr intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:toStr intoString:(reference)
    
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|() = 0 then return aParamStr
  
  
copy aParamStr to curStr
  
repeat with i in (anArray as list)
    set curStr to repChar(curStr, fromStr & i & toStr, "") of me
  end repeat
  
  
return curStr
end trimStrFromTo

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

★Click Here to Open This Script 

2017/11/28 指定のテキストからHTMLタグを除去

オープンソースの「NSString_striphtml」(By Leigh McCulloch)をFramework化したremoveTagKitを呼び出して指定文字列からHTMLタグを除去するAppleScriptです。

昔からこの種類のルーチンは存在していましたが、ちょっと高機能なものも欲しくなってきた今日このごろ。

いろいろ検討したところ、NSAttributedString経由でHTMLを解釈してテキストを取得する方法が手っ取り早くて(すでに組んであったので)いい感じでしたが、日本語の文字列が入ると文字化けが発生。

こちらのNSString_striphtmlのほうが18倍ぐらい高速で、改行タグが入っても改行として解釈されない点が自分の用途にかなっていました。

OS X 10.10以降用にビルドしたFrameworkのバイナリを用意したため、各自自己責任でFrameworkを~/Library/Frameworksフォルダに入れておためしください。

–> Download Framework Binary

AppleScript名:指定のテキストからHTMLタグを除去(removeTagKit)
– Created 2017-11-25 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “removeTagKit” –https://gist.github.com/leighmcculloch/1202238#file-nsstring_striphtml-m
–http://piyocast.com/as/archives/5002

set aStr to “<br />repeat〜end repeat”
set cStr to (current application’s NSString’s stringWithString:aStr)’s stripHtml() as string – under 0.001sec
–>  ”repeat〜end repeat”

★Click Here to Open This Script 

AppleScript名:指定のテキストからHTMLタグを除去(NSAttributedString)
– Created 2017-11-25 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/5002

set aStr to “<br />repeat〜end repeat”
set bStr to decodeHTMLasString(aStr) of me –0.004sec
(*

repeat
〜end repeat”
*)

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

★Click Here to Open This Script 

2017/11/28 2Dリストから、指定カラムのデータを削除する v3

2D List(配列変数)から、指定カラムのデータを削除するAppleScriptです。

Pure AppleScriptの機能の範囲でも、Cocoaの機能の範囲でも、結局は行ごとにループを回さないといけないようなので、もうちょっとクールな(ループなしで削除するような)解決策もありそうですが、どんなものでしょう。

AppleScript名:2Dリストから、指定カラムのデータを削除する v3
– Created 2017-11-25 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/5000

set aList to {{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}
set removeIndex to 3 –1 based index
set bList to removeColumnFrom2DArray(aList, removeIndex) of me
–>  {{1, 2}, {2, 3}, {3, 4}}

on removeColumnFrom2DArray(aList as list, removeIndex as integer)
  set newArray to {}
  
set realIndex to removeIndex - 1 –index conversion from 1-based to 0-based
  
repeat with i in aList
    set anArray to (current application’s NSMutableArray’s arrayWithArray:i)
    (
anArray’s removeObjectAtIndex:realIndex)
    
set the end of newArray to anArray as list of string or string –as anything
  end repeat
  
return newArray
end removeColumnFrom2DArray

★Click Here to Open This Script 

2017/11/25 applescript-stdlibに入っていた未知の機能(2)

記述内容に癖があって(技術的な難易度が高くて)解析しにくいapplescript-stdlib。実用性はいまひとつですが、技術を誇示するためのショーケースとしては古今例を見ないほど。その技術的な側面にスポットライトを当て、利用可能な要素技術をピックアップする記事の続編です。

script objectのネスティング

AppleScriptObjCのScriptにおいて、script文でその内部に別のAppleScriptObjCのScript Objectを定義することは、AppleScriptの処理系によって許可されていません(執筆時点での確認事項。Shane Stanleyとの間で事実確認ずみ)。

ただ、propertyへの高速アクセスを目的として、AppleScriptObjCのScript中にPure AppleScriptのScript Objectを宣言することはできていました。

applescript-stdlib中ではscript objectのネスティングなどが多用されており、その中には、Pure AppleScriptのScript中にAppleScriptObjCのscript objectを宣言して呼び出す、という使われ方がされていました。

たしかに、このやり方は試していなかったので、何かのときに利用できるかもしれません。

–Pure AppleScript
use AppleScript version “2.4″
use scripting additions

set aRes to testRun() of me

on testRun()
  
  script aScript
    —AppleScriptObjC
    
use framework “Foundation”
    
    on test2(aStr)
      set aProp to version of AppleScript
      
log aProp
      
set bStr to (reverse of characters of aStr) as string
      
return (current application’s NSString’s stringWithString:bStr) as string
    end test2
    
  end script
  
  —Pure AppleScript  
  
set bRes to aScript’s test2(“ABC”)
  
end testRun

★Click Here to Open This Script 

script objectをdelegateに指定

CocoaのGUI部品の中には、NSTableViewのようにGUI部品上でのイベント発生を指定したdelegateで受信する機能を持つものが多くあります。また、その際にはハンドラ名称を指定できるタイプのもの(NSTimerとか)もあれば、GUI部品側が指定した固定のハンドラを呼び出すものもあります。

ここでは、後者を対象として話をしますが・・・そのdelegate methodをscript文で論理分割した先のハンドラで受信する、という記述が行われていました。script文は使うと便利(巨大Script同士の結合とか)ですが、多用しすぎると問題を起こしやすそうな疑念があって(あと、見た目に難しそうに見えてメンテナンス時の心理的な障壁が上がってしまいそうな)、あまり使わないようにしてきました。

set responseBodyData to current application’s NSMutableData’s |data|()

script sessionTaskDelegate
  on URLSession:asocSession dataTask:asocTask didReceiveData:asocData
    responseBodyData’s appendData:asocData
  end URLSession:dataTask:didReceiveData:
end script

set asocSession to current application’s NSURLSession’s sessionWithConfiguration:sessionConfig delegate:sessionTaskDelegate delegateQueue:(missing value)

★Click Here to Open This Script 

handlerの存在確認

指定のScript Object中に指定名称のハンドラ(サブルーチン)が存在しているかどうかの確認機能が入っていました。無条件にリストアップが行えるわけではなく、指定名称のハンドラの存在確認ができるだけのようです。

「handler」なんていう予約語が存在していたことにビックリです。

set aRef to a reference to test1 of me
set aRes to hasHandler(aRef) of me
–> true

set bRef to a reference to test2 of me
set bRes to hasHandler(bRef) of me
–> false

on test1()
  
end test1

on hasHandler(handlerRef)
  try
    handlerRef as handler
    
return true
  on error number -1700
    return false
  end try
end hasHandler

★Click Here to Open This Script 

set aRef to a reference to test1_withData_ of me
set aRes to hasHandler(aRef) of me
–> true

set bRef to a reference to test0 of me
set bRes to hasHandler(bRef) of me
–>  true

–Objective-Cっぽいハンドラ
on test1:aParam withData:bParam
  
end test1:withData:

–無意味句を使った英文っぽいハンドラ
on test0 for aInt at bInt
  return (aInt + bInt)
end test0

on hasHandler(handlerRef)
  try
    handlerRef as handler
    
return true
  on error number -1700
    return false
  end try
end hasHandler

★Click Here to Open This Script 

実際、このhasHandlerによるハンドラの存在確認にはいろいろと制約条件があるようで、下記のようにさまざまな注意事項が書かれていました。また、ドキュメント化されていない仕様なので、トラブル源になる(OSのアップデート時にAppleにバグを作られる)可能性についても指摘があります。

– CAUTION: `hasHander` relies on AS handlers’ partial ability to behave as AS objects in that they can be retrieved by name, assigned to variables, and coerced to `handler` type. Be aware, however, this object-like behavior is undocumented and essentially undefined: AS handlers are not closures, so moving them around will completely break their lexical and dynamic bindings, causing seriously bizarre and incorrect behavior if subsequently called. The only reason `hasHandler` resorts to such hackery because AS lacks the introspection/stack trace capabilites to do the job right (either by asking the containing script object to describe its contents, or by calling the handler speculatively then examining the stack trace to determine if error -1708 was due to the handler not existing or a bug occurring within it).

– CAUTION: `hasHandler` only works for handlers with identifier-based names; do not use to check for existence of handlers with keyword-based names as that will result in incorrect behavior.

2017/11/24 applescript-stdlibに入っていた未知の機能(1)

applescript-stdlibは、記述内容に癖があって(技術的な難易度が高くて)解析しにくいので、とてもメンテナンスできない代物でしたが、部分的に解析してみると「宇宙人の乗ってきた宇宙船の部品」みたいな未知の機能がいろいろ入っていました。

NSObjectの判定

変数に入っている内容がCocoaのオブジェクト、総称するとNSObjectであるかどうかの判定機能が入っていました。

一般的に、Pure AppleScriptのオブジェクトであれば「class of 変数」で変数内容のクラス(型)を取得できますし、Cocoaのオブジェクトでもクラス名を取得することは可能です

ただ、事前にどちらの世界のオブジェクトなのかがわからないと対処のしようがありません。変数の中に入っているのがPure AppleScriptのオブジェクトなのかCocoaのオブジェクトなのかを判定する必要があるのです。

そこで、このようにして(↓)NSObjectであるかどうかを判定できます。結果が0でないとCocoaのオブジェクト(NSObject)と判定できます。

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set theValue to “10″
set aRes to (count {theValue} each reference)
–> 0

★Click Here to Open This Script 

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set theValue to current application’s NSString’s stringWithString:“ABC”
set aRes to (count {theValue} each reference)
–> 1

★Click Here to Open This Script 

as anything

これは見たこともない記述です。castするときに、元のデータ型のままにするという意味のようです。AppleのWebサイトにも記述がなく、860ページもある書籍「AppleScriptリファレンス」にも、Appleが出版していた書籍「AppleScript言語ガイド」にも記載はありません(この時点で知ったので、自分の本にも記載はありません)。

長年、全世界のAppleScriptの情報を収集、分類、評価してきましたが、正直この記述に出会ったのはこれがはじめてです。にもかかわらず、明確にAppleScriptの予約語として存在していることが(Script Editorの構文色分け機能で)確認できました。

「こんな得体の知れない機能をどこで使うのか?」という話になりますが、AppleScriptのオブジェクトにcast可能なNSObject(NSArray, NSDictinaryやNSStringなどなど)がそのままの状態でPure AppleScriptのパートに受け渡されることを防ぐために記述するようです。

as anythingでcastすると、Pure AppleScriptのオブジェクトはそのままの状態で、Cocoaのオブジェクトはcast可能なPure AppleScriptのオブジェクトにcastされます。

では、Pure AppleScriptのオブジェクトに変換できないCocoaオブジェクトはどうなるのかという話になりますが、変換できずそのままです。

set b to 10
set a to b as anything
–> 10

set c to “ABC”
set d to c as anything
–> “ABC”

set e to missing value
set f to e as anything
–> missing value

★Click Here to Open This Script 

これを、ASOCのScript中で記述してコンパイル(構文確認+中間言語変換)を行うと、「as anything」が「as list of string or string」に置き換えられます。

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set b to 10
set a to b as list of string or string –as anythingと書いておいたものが、構文確認時に置換される
–> 10

set c to current application’s NSString’s stringWithString:“ABC”
set d to c as list of string or string
–> “ABC”

set e to current application’s NSIndexSet’s new()
set f to e as list of string or string
–>  (_NSCachedIndexSet) <_nscachedindexset : 0x60800042c900>(no indexes)–Cast不能なCocoaオブジェクト

★Click Here to Open This Script 

「 list of string」({”111″, “2222″}) or 「string」(”1111″)といっておきながら数値がそのまま通るあたりに記述内容と実行結果の乖離を感じますが、元がanythingだと考えればそういうものなんでしょう。コメントなしにこの記述が書かれていたら首をひねってしまうところです。

一応、Classic Macエミュレータの「SheepSaver」上で動作するClassic Mac OS 8.6上のAppleScript J1-1.3.7で「as anything」の動作を確認してみたら、問題なく動作していました。そんな昔からあったのか〜(ーー;;;

anything3.png

ASCObjC ExplorerのScript用語辞書の作成機能で指定可能なデータ型を確認してみたら、「any」という項目がありました。このanyがanythingに該当するものだろうかと。

anything4.png

ただ、こういう「マニアックかつマイナーな仕様」は実際にテストしてみないと本当に使えるのかどうか不明です(とくに、英語環境以外では)。

実際のところ、applescript-stdlibの中には日本語環境で構文確認が通らないもの(TestTools.scptd)もあり、あまりマニアックな仕様に走りすぎると動作すらしないという見本にもなっています。

2017/11/24 Mark Alldritの「MarksLib」

AppleScript DebuggerのメーカーであるLate Night SoftwareのMark Alldrittによる「MarksLib」(Version 1.0)がTwitter上で紹介されていました。

リリースされたのは今年の5月らしく、リリースされてからけっこうな日がたっていたようです(知らなかった〜)。

AppleScript Librariesについては幾人かのデベロッパーが公開し、日々利用しています。事実上、OSAXの代替のような存在になっており、AppleScriptにまとまった拡張機能を提供するものとして機能しています。

ただ、作成・提供する側のメリットが少なく(フリー配布だと)、何かのソフトウェアや書籍を販売するための「呼び水」として利用できる開発者のみが提供している、という印象。

# 自分もコマンドラインからosascript経由でAppleScriptを呼び出す書籍を企画していたときに、osascript環境にたりない機能を補う「piyoLib」を作成していましたが、企画が流れてお蔵入りしています

そんな中、最も有名なのはShane Stanleyによるライブラリで、多種多用かつ高機能な内容です。どちらかといえば「AppleScriptObjCを覚えたユーザー」向けの高度な内容といえます。

Markのライブラリは、Shaneのものよりも基礎的な内容をねらったもので、AppleScriptObjCもCocoaもわからなくても利用できるものです。

一番の特徴は、Github上でオープンソースで公開されており、インストール方法もコマンドライン上から操作するだけ。「こういうやり方もあるのか」と、参考になります。

MarkLib v1.0にはAppleScript用語辞書(sdef)はついていません。

ためしに、MarksLib中のreadFromFile()ハンドラで各種文字エンコーディングのテキストの読み込みを試してみたところ、シフトJISのみ読み込めました(UTF-8をはじめ、他のエンコーディングも全滅)。日本語環境では実用性はありません。

内容を読んでみても「とりあえず動く」というレベルの最低限のものなので、Script Librariesの「サンプル」というレベルだと理解しました。

とりあえず、Github上でオープンソースのライブラリを公開して、配布するという「やりかた」が参考になりそうです。

2017/11/22 Finder上で選択中の画像を横方向に連結

Finder上で選択中の画像ファイルを横方向に連結してデスクトップにPNG形式で書き出すAppleScriptです。

Finder上で選択中の画像ファイルに対して、

finder_selection.png

それらが画像ファイルかどうかを確認し、画像ファイルであれば横方向に(10pointの隙間を作って)連結してデスクトップ上にPNG形式で書き出します。

c524ba57-f107-4605-92c9-dfb95720cf31_resized.png

こんなふうに(↑)。

Retina解像度対策(x2)は行なっていないので、解像度の異なる画像同士を連結しようとすると問題が(解像度の不一致による極端なサイズの違い)出る可能性があります。

なお、指定パスからのUTIツリーの取得にオープンソースのフレームワーク「MagicKit」を使用しています。本Scriptの実行にはGithub上のプロジェクトをダウンロードして各自でXcode上でFrameworkをビルドし、MagicKit.frameworkを~/Library/Frameworksフォルダ以下にインストールする必要があります。

AppleScript名:Finder上で選択中の画像を横方向に連結
– Created 2017-11-21 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “QuartzCore”
use framework “AppKit”
use framework “MagicKit” –https://github.com/aidansteele/magickit
–http://piyocast.com/as/archives/4995

property NSMutableArray : a reference to current application’s NSMutableArray
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSUUID : a reference to current application’s NSUUID
property NSPNGFileType : a reference to current application’s NSPNGFileType
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property NSImage : a reference to current application’s NSImage
property |NSURL| : a reference to current application’s |NSURL|
property GEMagicKit : a reference to current application’s GEMagicKit

property xGap : 10 –連結時の画像間のアキ(横方向)

tell application “Finder”
  set aSel to selection as alias list
  
if aSel = {} or aSel = “” then return
end tell

–選択した画像をArrayに入れる
set imgList to NSMutableArray’s new()
repeat with i in aSel
  set aPath to POSIX path of i
  
  
–指定ファイルのUTIを取得して、画像(public.image)があれば処理を行う
  
set aRes to (GEMagicKit’s magicForFileAtPath:aPath)
  
set utiList to (aRes’s uniformTypeHierarchy()) as list
  
if “public.image” is in utiList then
    set aNSImage to (NSImage’s alloc()’s initWithContentsOfFile:aPath)
    (
imgList’s addObject:aNSImage)
  end if
end repeat

–KVCで画像の各種情報をまとめて取得
set sizeList to (imgList’s valueForKeyPath:“size”) as list –NSSize to list of record conversion
set maxHeight to ((NSArray’s arrayWithArray:sizeList)’s valueForKeyPath:“@max.height”) as real
set totalWidth to ((NSArray’s arrayWithArray:sizeList)’s valueForKeyPath:“@sum.width”) as real
set totalCount to ((NSArray’s arrayWithArray:sizeList)’s valueForKeyPath:“@count”) as integer

–出力画像作成
set tSize to current application’s NSMakeSize((totalWidth + (xGap * totalCount)), maxHeight)
set newImage to NSImage’s alloc()’s initWithSize:tSize

–順次画像を新規画像に上書き
set xOrig to 0
repeat with i in (imgList as list)
  set j to contents of i
  
set curSize to j’s |size|()
  
set aRect to {xOrig, (maxHeight - (curSize’s height())), (curSize’s width()), (curSize’s height())}
  
set newImage to composeImage(newImage, j, aRect) of me
  
set xOrig to (curSize’s width()) + xGap
end repeat

–デスクトップにPNG形式でNSImageをファイル保存
set aDesktopPath to current application’s NSHomeDirectory()’s stringByAppendingString:“/Desktop/”
set savePath to aDesktopPath’s stringByAppendingString:((NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.png”)
set fRes to saveNSImageAtPathAsPNG(newImage, savePath) of me

–2つのNSImageを重ね合わせ合成してNSImageで返す
on composeImage(backImage, composeImage, aTargerRect)
  set newImage to NSImage’s alloc()’s initWithSize:(backImage’s |size|())
  
  
copy aTargerRect to {x1, y1, x2, y2}
  
set bRect to current application’s NSMakeRect(x1, y1, x2, y2)
  
  
newImage’s lockFocus()
  
  
set newImageRect to current application’s CGRectZero
  
set newImageRect’s |size| to (newImage’s |size|)
  
  
backImage’s drawInRect:newImageRect
  
composeImage’s drawInRect:bRect
  
  
newImage’s unlockFocus()
  
return newImage
end composeImage

–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

★Click Here to Open This Script 

2017/11/22 NSColorのカラースペース変換

Cocoaの機能を用いてカラースペース変換を行なってみたAppleScriptです。

あまり本気で使うつもりはない機能ですが、ものは試しということで動作確認してみました。

c524ba57-f107-4605-92c9-dfb95720cf31_resized.png

選択色をRGBから指定のカラースペース中の色に変換し、それをふたたびRGBに戻して色表示を行います。

AppleScript名:選択色をカラースペース(RGB→グレースケール→RGB)変換して比較
– Created 2017-11-21 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4993

set {rCol, gCol, bCol} to choose color

set rCol to rCol / 65535
set gCol to gCol / 65535
set bCol to bCol / 65535
log {rCol, gCol, bCol}

set aCol to current application’s NSColor’s colorWithCalibratedRed:rCol green:gCol blue:bCol alpha:1

set bCol to aCol’s colorUsingColorSpaceName:“NSDeviceWhiteColorSpace”
if bCol = missing value then error “Color Space Conversion Error (1)”

set cCol to bCol’s colorUsingColorSpaceName:“NSDeviceRGBColorSpace”
if cCol = missing value then error “Color Space Conversion Error (2)”

set r2Col to (cCol’s redComponent())
set g2Col to (cCol’s greenComponent())
set b2Col to (cCol’s blueComponent())
log {r2Col, g2Col, b2Col}

choose color default color {r2Col * 65535, g2Col * 65535, b2Col * 65535}

★Click Here to Open This Script 

AppleScript名:選択色をカラースペース(RGB→CMYK→RGB)変換して比較
– Created 2017-11-21 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4993

set {rCol, gCol, bCol} to choose color

set rCol to rCol / 65535
set gCol to gCol / 65535
set bCol to bCol / 65535
log {rCol, gCol, bCol}

set aCol to current application’s NSColor’s colorWithCalibratedRed:rCol green:gCol blue:bCol alpha:1

set bCol to aCol’s colorUsingColorSpaceName:“NSDeviceCMYKColorSpace”
if bCol = missing value then error “Color Space Conversion Error (1)”

set cCol to bCol’s colorUsingColorSpaceName:“NSDeviceRGBColorSpace”
if cCol = missing value then error “Color Space Conversion Error (2)”

set r2Col to (cCol’s redComponent())
set g2Col to (cCol’s greenComponent())
set b2Col to (cCol’s blueComponent())
log {r2Col, g2Col, b2Col}

choose color default color {r2Col * 65535, g2Col * 65535, b2Col * 65535}

★Click Here to Open This Script 

2017/11/21 twoDdictKitのじっけん v1

オープンソースの「AGTTwoDimensionalDictionary」(By Agant Ltd.)をフレームワーク化したtwoDdictKit.frameworkを呼び出すAppleScriptです。

AGTTwoDimensionalDictionary「kd木」を実装したもので、 2次元ユークリッド空間の座標を扱っています。2次元座標を扱うものの、配列変数ではないので0.1とか-10といった整数値ではない数値による座標を指定可能です。

と自分で書いておきながら「これって何に使えるの?」という疑問からテストしたものだったのと、本フレームワーク自体がそれほど機能を実装されているものではないため、現時点では「とりあえずためしてみました」レベル以上のものではありません。

掲載のAppleScriptでは、サブルーチンとの間のデータのやりとりを値渡しではなく参照渡しで行っています。ふだんは、なるべく単純なプログラムとデータ構造で記述するようにしているので、極力参照渡しは避けているのですが、このデータについては参照渡しでないと手が出なかったもので。

twoDdictKit.frameworkをmacOS 10.10以降用にビルドしたバイナリを用意しましたので、自己責任で~/Library/Frameworksフォルダに入れて使ってみてください。

–> Download towddictkit.framework binary

AppleScript名:twoDdictKitのじっけん v1
– Created 2016-03-03 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “towDdictKit” –https://github.com/AgantLtd/twoddict
–http://piyocast.com/as/archives/4991

set aDict to current application’s AGTTwoDimensionalDictionary’s new()
setData(aDict, “Hello”, 0, 0) of me
set aRes to (getData(aDict, 0, 0) of me) as string
–>  ”Hello”

setData(aDict, “Goodbye”, 1, 1) of me
set aRes to (getData(aDict, 1, 1) of me) as string
–>  ”Goodbye”

removeData(aDict, 1, 1) of me
set aRes to (getData(aDict, 1, 1) of me) as string
–>  ”missing value”

–Set Data By Reference
on setData(aDict, aData, aDim, bDim)
  aDict’s setObject:aData atLocation:(addr(aDim, bDim) of me)
end setData

–Return Data By Reference
on getData(aDict, aDim, bDim)
  set aVal to aDict’s objectAtLocation:(addr(aDim, bDim) of me)
end getData

–Return Data By Reference
on removeData(aDict, aDim, bDim)
  aDict’s removeObjectAtLocation:(addr(aDim, bDim) of me)
end removeData

on addr(d1 as number, d2 as number)
  return current application’s CGPointMake(d1, d2)
end addr

★Click Here to Open This Script 

2017/11/20 主要なタイムゾーンのカレンダーの開始曜日を取得して集計

macOS内に定義されているタイムゾーン(knownTimeZoneNames)のカレンダーの開始曜日を取得して、firstWeekdayを集計するAppleScriptです。

取得するScriptに集計部分を追加しただけのものです。

AppleScript名:主要なタイムゾーンのカレンダーの開始曜日を取得して集計
– Created 2017-11-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4990

property NSDate : a reference to current application’s NSDate
property NSCalendar : a reference to current application’s NSCalendar
property NSOrderedSet : a reference to current application’s NSOrderedSet
property NSLocale : a reference to current application’s NSLocale
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSTimeZone : a reference to current application’s NSTimeZone

set currentCalendar to NSCalendar’s currentCalendar()
set tzList to (NSTimeZone’s knownTimeZoneNames()) as list

set fList to {}
repeat with i in tzList
  set j to contents of i
  (
currentCalendar’s setLocale:(NSLocale’s localeWithLocaleIdentifier:j))
  
  
set aTZ to (NSTimeZone’s timeZoneWithName:j)
  
set theComponents to (currentCalendar’s componentsInTimeZone:aTZ fromDate:(NSDate’s |date|()))
  
set tmpCalend to theComponents’s calendar()
  
  
set aFirstDay to (tmpCalend’s firstWeekday) as integer –firstWeekday: 1=Sunday, 2=Monday
  
set the end of fList to aFirstDay
end repeat

set aRes to calcFrequency(fList) of me
–> {{theKey:1, theCount:371}, {theKey:2, theCount:66}}

–1D Listのデータを各要素ごとに出現頻度集計
on calcFrequency(fList)
  set f2List to makeUniqueListFrom(fList) of me
  
  
set theCountedSet to NSCountedSet’s alloc()’s initWithArray:fList
  
set newArray to NSMutableArray’s new()
  
repeat with i in f2List
    (newArray’s addObject:{theKey:i, theCount:(theCountedSet’s countForObject:i)})
  end repeat
  
return newArray as list
end calcFrequency

–リスト内容のユニーク化
on makeUniqueListFrom(theList)
  set theSet to NSOrderedSet’s orderedSetWithArray:theList
  
return (theSet’s array()) as list
end makeUniqueListFrom

★Click Here to Open This Script 

2017/11/20 主要なタイムゾーンのカレンダーの開始曜日を取得

macOS内に定義されているタイムゾーン(knownTimeZoneNames)のカレンダーの開始曜日を取得するAppleScriptです。

macOS内に定義されているタイムゾーンは437あり、当然のことながら時差は±12h(24のタイムゾーン)しかないので、それぞれのタイムゾーンの「時差」は重複しています。

どちらかといえば、各国が採用しているカレンダーの仕様(どの曜日からはじまるか)を調べるために書いてみたものです。各国の首都が所在しているタイムゾーンを求め、そこのカレンダーの仕様を調べられるとよかったのですが、OS内部のAPIだけでは実現できなかったので、かわりに「knownTimeZoneNames」を呼び出してみた次第です。

週の開始日を示すfirstWeekdayは1:Sunday, 2:Monday….となっており、多くのT imezoneがSundayであることが見てとれます。

自分が位置しているタイムゾーン「Asia/Tokyo」は2(=Monday)とありました。

ただし、実際にシステム環境設定で「言語と地域」をオープンしてみると、

system_resized.png

日曜日(Sunday)。個人的にも日曜日はじまりで違和感がありません。はて?

ちなみに、1週間の開始曜日は海外のクライアントと仕事をするうえで、(日本の日常会話における)天気の話なみに重要な項目です。「常識」が常識ではないことの確認を行うことは重要です。

AppleScript名:主要なタイムゾーンのカレンダーの開始曜日を取得
– Created 2017-11-14 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4988

set currentCalendar to current application’s NSCalendar’s currentCalendar()
set knownTimeZoneNameList to current application’s NSTimeZone’s knownTimeZoneNames() as list

set fList to {}
repeat with i in knownTimeZoneNameList
  set j to contents of i
  (
currentCalendar’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:j))
  
  
set aTZ to (current application’s NSTimeZone’s timeZoneWithName:j)
  
set theComponents to (currentCalendar’s componentsInTimeZone:aTZ fromDate:(current application’s NSDate’s |date|()))
  
set tmpCalend to theComponents’s calendar()
  
set aFirstDay to (tmpCalend’s firstWeekday) as integer
  
set the end of fList to aFirstDay
end repeat

return fList –firstWeekday: 1=Sunday, 2=Monday

★Click Here to Open This Script 

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

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

flowerchar3_resized.png

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

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

flowerchar2_resized.png

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

2017-11-18-22_12_04.gif

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

★Click Here to Open This Script 

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

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

set aString to “ぴ”

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

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

–花文字文字列を計算して返す
on getHanamojiStr(aFontSize, aFontName, aString, aThread, incFontName)
  if length of aString is not equal to 1 then return false
  
  
set aThreadShould to 768 * aThread
  
  
set fillColor to NSColor’s whiteColor –塗り色
  
set bString to aString & ” “
  
set anAssrStr to makeRTFfromParameters(bString, aFontName, aFontSize, -2, (aFontSize * 1.2)) of me
  
set aSize to anAssrStr’s |size|()
  
  
if class of aSize = record then
    set attrStrWidth to width of aSize
    
set attrStrHeight to height of aSize
  else if class of aSize = list then –macOS 10.13.xのバグ回避
    copy aSize to {attrStrWidth, attrStrHeight}
  end if
  
  
set {xPos, yPos} to {0, 0}
  
  
–下地の画像を作成
  
set tmpImg1 to makeImageWithFilledColor(attrStrWidth, attrStrHeight, fillColor) of me
  
  
–下地の画像の上にAttributed Stringを描画
  
set tmpImg2 to drawAttributedStringsOnImage(tmpImg1, anAssrStr, xPos, yPos) of me
  
  
–NSImageからRaw画像を作成
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:(tmpImg2’s TIFFRepresentation())
  
  
–画像から順次指定座標の色データを拾って花文字listに反映
  
set strList to {}
  
repeat with y from 1 to attrStrHeight - 1
    
    
set strListX to {}
    
repeat with x from 0 to attrStrWidth - 1
      set tmpCol to getColorFromRawImage(aRawimg, x, y) of me
      
      
if tmpCol is not equal to false then
        if tmpCol is not equal to {255, 255, 255} then
          copy tmpCol to {tmpR, tmpG, tmpB}
          
if (tmpR + tmpG + tmpB) < aThreadShould then
            set the end of strListX to aString
          else
            set the end of strListX to “ ”
          end if
        else
          set the end of strListX to “ ”
        end if
      end if
      
    end repeat
    
set the end of strList to strListX
  end repeat
  
  
–2D List→Text
  
set aRes to list2dToStringByUsingDelimiters(strList, “ ”, return) of me
  
  
if incFontName = true then
    set fName to getDisplayedNameOfFont(aFontName) of me
    
set aRes to “■” & fName & return & return & aRes
  end if
  
  
return aRes
end getHanamojiStr

–指定Raw画像中の指定座標のピクセルの色をRGBで取り出す
on getColorFromRawImage(aRawimg, x, y)
  set origColor to (aRawimg’s colorAtX:x y:y)
  
set srgbColSpace to NSColorSpace’s deviceRGBColorSpace
  
if srgbColSpace = missing value then return false
  
  
set aColor to (origColor’s colorUsingColorSpace:srgbColSpace)
  
  
set aRed to (aColor’s redComponent()) * 255
  
set aGreen to (aColor’s greenComponent()) * 255
  
set aBlue to (aColor’s blueComponent()) * 255
  
  
return {aRed as integer, aGreen as integer, aBlue as integer}
end getColorFromRawImage

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

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

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

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

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

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

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

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

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

★Click Here to Open This Script 

2017/11/17 指定文字の花文字を取得 v1

指定文字の花文字を取得するAppleScriptです。

hanamoji1.png

指定文字のStyledStringを作成して画像に描画し、各座標の色データを取得して判定しています。こんな処理がAppleScriptだけで記述できるようになったことは、素直に驚きです。

ブランクのNSImageにスタイル付きテキストで描画し、Raw画像の各座標からカラーデータを抽出して2D Listに反映させ、最終テキストに変換して0.1秒(MacBook Pro Retina 2012 Core i7 2.66GHz)ぐらいです。色ピックアップ部分がオーバーヘッドになっていると思います。

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

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

set aString to “長”
set fRes to getHanamojiStr(24, “HiraginoSans-W0″, aString) of me

–花文字文字列を計算して返す
on getHanamojiStr(aFontSize, aFontName, aString)
  if length of aString is not equal to 1 then return false
  
  
set fillColor to NSColor’s whiteColor –塗り色
  
set bString to aString & ” “
  
set anAssrStr to makeRTFfromParameters(bString, aFontName, aFontSize, -2, (aFontSize * 1.2)) of me
  
set aSize to anAssrStr’s |size|()
  
  
if class of aSize = record then
    set attrStrWidth to width of aSize
    
set attrStrHeight to height of aSize
  else if class of aSize = list then –macOS 10.13.xのバグ回避
    copy aSize to {attrStrWidth, attrStrHeight}
  end if
  
  
set {xPos, yPos} to {0, 0}
  
  
–下地の画像を作成
  
set tmpImg1 to makeImageWithFilledColor(attrStrWidth, attrStrHeight, fillColor) of me
  
  
–下地の画像の上にAttributed Stringを描画
  
set tmpImg2 to drawAttributedStringsOnImage(tmpImg1, anAssrStr, xPos, yPos) of me
  
  
–NSImageからRaw画像を作成
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:(tmpImg2’s TIFFRepresentation())
  
  
–画像から順次指定座標の色データを拾って花文字listに反映
  
set strList to {}
  
repeat with y from 2 to attrStrHeight - 2
    set strListX to {}
    
repeat with x from 0 to attrStrWidth - 1
      set tmpCol to getColorFromRawImage(aRawimg, x, y) of me
      
      
if tmpCol is not equal to false then
        if tmpCol is not equal to {255, 255, 255} then
          copy tmpCol to {tmpR, tmpG, tmpB}
          
if (tmpR + tmpG + tmpB) < 500 then
            set the end of strListX to aString
          else
            set the end of strListX to “ ”
          end if
        else
          set the end of strListX to “ ”
        end if
      end if
      
    end repeat
    
set the end of strList to strListX
  end repeat
  
  
–2D List→Text
  
set aRes to list2dToStringByUsingDelimiters(strList, “ ”, return) of me
  
return aRes
end getHanamojiStr

–指定Raw画像中の指定座標のピクセルの色をRGBで取り出す
on getColorFromRawImage(aRawimg, x, y)
  set origColor to (aRawimg’s colorAtX:x y:y)
  
set srgbColSpace to NSColorSpace’s deviceRGBColorSpace
  
if srgbColSpace = missing value then return false
  
  
set aColor to (origColor’s colorUsingColorSpace:srgbColSpace)
  
  
set aRed to (aColor’s redComponent()) * 255
  
set aGreen to (aColor’s greenComponent()) * 255
  
set aBlue to (aColor’s blueComponent()) * 255
  
  
return {aRed as integer, aGreen as integer, aBlue as integer}
end getColorFromRawImage

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

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

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

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

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

★Click Here to Open This Script 

2017/11/16 指定UTIでUTIを入れたリストをフィルタリング

指定のUTI(Uniform Type Identifier)で、UTIを入れたリストをフィルタリングするAppleScriptです。

UTIはデータのタイプを一意に識別する文字列で、「ざっくりとしたデータ区分」から「詳細なデータ区分」まで階層的に表現できます。AppleScriptではchoose fileコマンドに「of type」のオプションがあり、ここにUTIを記述する・・・という程度のつながり。JPEG画像のみ、PDFのみファイル一覧ダイアログに表示、といった「詳細なデータ型の抽出」機能が用いられてきました。

ただ、UTI自体はもうちょっと便利な概念なので、AppleScriptでもCocoaの機能やSpotlightの機能を日常的に利用するようになってきた今日においては、より活用すべき仕組みです。

たとえば、 “public.mp3″とUTIを指定したらMP3データしか対象になりませんが、より上位の概念を示す”public.audiovisual-content”と指定すれば、さまざまな形式のオーディオ、ビデオのデータファイルが対象になります。

こうしたUTIの概念ツリーを使って「このカテゴリに含まれるデータ型はどれ?」といった演算が手軽にできるようになっているため、調べてまとめておきました(探すのには苦労しましたけれど)。

本Scriptでは、 峪慊蠅靴UTi」に含まれるものを◆UTIのリスト」から抽出します。爬虫類に含まれるものをトカゲ、ヘビ、カエルのリストに対して抽出すると、トカゲとヘビが返ってくるような感じです。

ちなみに、ファイル拡張子からUTIを求める機能はShane StanleyのAppleScript Libraries「Bridge Plus」が提供しています。

AppleScript名:指定UTIでUTIを入れたリストをフィルタリング
– Created 2017-11-03 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4978

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

set aList to {“public.jpeg”, “com.compuserve.gif”, “public.svg-image”, “public.plain-text”, “com.apple.iwork.keynote.key”, “com.apple.iwork.pages.pages”, “com.apple.iwork.numbers.numbers”, “com.microsoft.word.doc”, “com.microsoft.excel.xls”, “com.microsoft.powerpoint.ppt”, “com.apple.mail.email”, “com.apple.applescript.script”, “com.apple.applescript.text”, “public.html”, “com.apple.property-list”, “public.zip-archive”, “public.au-audio”, “com.apple.m4a-audio”, “com.apple.m4v-video”}

set aRes to filterUTIList(aList, “public.text”)
–>  {”public.plain-text”, “com.apple.applescript.script”, “com.apple.applescript.text”, “public.html”}

set bRes to filterUTIList(aList, “public.image”)
–>  {”public.jpeg”, “com.compuserve.gif”, “public.svg-image”}

set cRes to filterUTIList(aList, “public.audiovisual-content”)
–>  {”public.au-audio”, “com.apple.m4a-audio”, “com.apple.m4v-video”}

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 

2017/11/14 相対パスから絶対パスを計算して求める v2

相対パスから絶対パスを計算して求める( 峇霆爐箸覆訐簑丱僖后廚鮓気豊◆嵳燭┐蕕譴秦蠡丱僖后廚鬮「絶対パス」に変換する)AppleScriptです。

Markdown書類にリンクした画像のリンク切れチェックを行う必要があり、あまり考えずに作ってみました。

Markdown書類中にリンクした画像は相対パスでリンク先を記述しています。リンクについては指定画像を画像専用フォルダにコピーして相対パスを求め、リンクタグをクリップボードに入れるAppleScriptを書いて(リンクを)記述しています。

このとき、Markdown書類群のフォルダ階層構造を維持していればリンク切れは起こりませんが、一応チェックしておきたいのが人情。

そこで、チェック用のScriptを書いておくことに。だいたいはありあわせのサブルーチンを組み合わせることでSpotlight経由のMarkdown書類の検索や正規表現によるMarkdown中の画像リンク箇所の抽出など、たいした手間もかけずに記述できます。

ただ、Markdown書類のパス(絶対パス)をもとに、書類中に書かれている相対パスから実際にリンク画像が存在している絶対パスを求めるサブルーチンが存在していませんでした(OS側に用意されていてもよさそうなものですが)。

そこで、本ルーチンを書いて使ってみました。とくに問題もなく使えています。

AppleScript名:相対パスから絶対パスを計算して求める v2
– Created 2017-11-11 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4976

set absolutePath to “/Users/me/Documents/–Book 1「AppleScript最新リファレンス バージョン2」/5000 iOSデバイスとの連携/5100 iOSデバイスからMacに画面を出力するAirServer.md”
set relativePath to “../9999_images/img-1.jpeg”

set relativePath to calcAbsolutePath(absolutePath, relativePath) of me
–>  ”/Users/me/Documents/–Book 1「AppleScript最新リファレンス バージョン2」/9999_images/img-1.jpeg”

on calcAbsolutePath(aAbsolutePOSIXfile, bRelativePOSIXfile)
  set aStr to current application’s NSString’s stringWithString:aAbsolutePOSIXfile
  
set bStr to current application’s NSString’s stringWithString:bRelativePOSIXfile
  
  
set aList to aStr’s pathComponents() as list
  
set bList to bStr’s pathComponents() as list
  
  
set aLen to length of aList
  
  
set aCount to 1
  
repeat with i in bList
    set j to contents of i
    
if j is not equal to “..” then
      exit repeat
    end if
    
set aCount to aCount + 1
  end repeat
  
  
set tmp1List to items 1 thru (aLen - aCount) of aList
  
set tmp2List to items aCount thru -1 of bList
  
  
set allRes to current application’s NSString’s pathWithComponents:(tmp1List & tmp2List)
  
  
return allRes as text
end calcAbsolutePath

★Click Here to Open This Script 

2017/11/11 Metadata Lib 2.0

Shane StanleyによるSpotlight検索用のAppleScript Libraies「Metadata Lib」の新バージョン2.0が公開されました。

Metadata Libは登場頻度も高く、実際に便利であるため、万人におすすめできるライブラリです。「おすすめできないライブラリってあるのかよ?」という話もありますが、癖が強くて読みにくくて特定の言語環境(英語とか)でしか検証が行われていないライブラリは避けています。

Metadata Lib v1.0はmacOS 10.9以降を対象としていましたが、v2.0はmacOS 10.10以降を対象としています。正直、10.10はバグが多くて避けたいので、10.10以降と考えてもよいでしょう(macOS 10.10嫌い。でも、バグだらけで直る見通しすら立っていないmacOS 10.13が一番嫌い)。

Metadata Lib 1.0から2.0への変更点は、AppleScript用語辞書(sdef)が添付され、英語っぽい記法で呼び出せるようになった点です。バージョン1.0の「いわゆるサブルーチンの塊を呼び出している感」から、英文っぽいこなれた表記ができるようにテイストが変わりました。

いっぽうで、Metadata Lib v1.0を対象に書いたAppleScriptも、v2.0でそのまま使えます。

mdfind2.png

また、用例(Sample Searches.scpt)が添付されるようになったのも大きな違いです。

mdfind_resized.png

本ライブラリは実際に内容(ソース)を読むこともできるようになっており、用語辞書の用語を使って呼び出した場合とバージョン1.0的な普通のハンドラの両方が用意されていることが読み取れます。

AppleScript LibrariesはAppleScriptによってAppleScriptの予約語を拡張して機能を追加できるという、OSAXをAppleScript自身で書けるような存在で、ちょっとやりすぎな特徴として「自分自身のAppleScript用語辞書の用語を用いてライブラリを記述できる」というものがあります。

ただ、ライブラリ自身の用語辞書を用いてライブラリの内容を書くとメンテナンスがやりにくくなるので避けるべきです。

今回のMetadata Libのように「用語を使ったハンドラ」と「通常のサブルーチン的なハンドラ」の両方を用意して、基本的には通常のサブルーチンを呼び出すような記法で書いてあり、よいお手本となる内容です。

use AppleScript version “2.4″ – 10.10 or later
use framework “Foundation”
use mdLib : script “Metadata Lib” version “2.0.0″

–添付のAppleScript用語辞書を利用した英文っぽいハンドラ
on perform search predicate string predString in folders filesAliasesURLsOrPosixPaths : missing value just in fileAliasURLOrPosixPath : missing value in scopes listOfScopes : missing value search arguments argList : {} attributes to include attList : missing value converting dates datesFlag : false names only namesFlag : false
  –  
end perform search

–普通のサブルーチンっぽい(Objective-Cっぽい記法の)ハンドラ
on searchFolders:filesAliasesURLsOrPosixPaths searchString:predString searchArgs:argList
  
end searchFolders:searchString:searchArgs:

★Click Here to Open This Script 

ささっとMetadata Libの中を読んでみて驚いたのが、「using terms from scripting additions」という記述。これは見たことがありません(using termis from application “Finder” といったようにアプリケーション名を指定するものはありました)。

using terms from scripting additions
  tell (current date) to set {theASDate, year, day, its month, day, its hours, its minutes, its seconds} to {it, theYear, 1, theMonth, theDay, theHour, theMinute, theSeconds}
end using terms from

★Click Here to Open This Script 

2017/11/11 1Dリスト中の最も近い値を返す

1次元配列(1D List)に入っている数値のうち、パラメータの数値が最も近いものを返すAppleScriptです。

1D Listに入っている数値の刻み幅が一定でない場合(人間には見た目がすっきりしていても刻み幅が一定でないなど)に、最も近い値のものを自動でピックアップすることを目的としています。

AppleScript名:1Dリスト中の最も近い値を返す
– Created 2017-11-11 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4971
property NSArray : a reference to current application’s NSArray

set aList to {50, 30, 20, 15, 10, 5, 0}

set targNum to retNearestNumInList(7, aList) of me
–>  5

set targNum to retNearestNumInList(100, aList) of me
–> 50

on retNearestNumInList(aNum, aList as list)
  set anNSArray to NSArray’s arrayWithArray:aList
  
set bList to (anNSArray’s sortedArrayUsingSelector:“compare:”) as list
  
set aPreNum to 0
  
  
repeat with i in bList
    set j to contents of i
    
if j aNum then
      exit repeat
    end if
    
set aPreNum to j
  end repeat
  
  
return aPreNum
end retNearestNumInList

★Click Here to Open This Script 

2017/11/10 選択した画像を画像フォルダにコピーして、Markdown書式で画像パスタグをクリップボードに転送 v2

指定の画像を、書いている書籍の画像フォルダにコピーして、作成中のMarkdown書類からの相対パスを求めてMarkdown形式の画像リンクタグをクリップボードに転送するAppleScriptです。

md1.png

書籍の執筆作業は、「–」ではじまる名前の書籍ルートフォルダ以下に章ごとのフォルダを作成し、その中にMarkdown書類やPages書類を入れています。

画像については各原稿と同じフォルダ内ではなく、画像専用フォルダに配置(一般的なWebサイトの管理と同じ)。各Markdown書類には画像専用フォルダへの相対パスで画像リンクタグを入れています。

![md1.png](../9999_images/md1.png)

md2.png

本AppleScriptは、この書籍中の書籍中の画像専用フォルダに指定画像をコピーし、クリップボードに対して画像の相対パスを入れます。

書籍ルートフォルダ以下の画像専用フォルダを検索するためにSpotlight検索用の「MetaData Lib」を使用しています。

ファイルコピー時には画像専用フォルダ内のすべてのファイル名をチェックし、ファイル名の重複が発生していた場合にはファイル名末尾に連番をつけ、重複を回避しています。

前提としているMarkdownエディタは「MacDown」で、最前面の書類のパスを取得する部分でMacDownの機能を用いています。ただ、その程度の機能しか利用していないので、他のMarkdownエディタ用に転用するのも難しくないと思います(MarkdownエディタでAppleScriptに対応しているものが少数派なんですが)。

以前はMarkdown書類中にリンクする画像はDropboxにREST API経由でアップロードしてURLを用いていました。アップロード自体はそれなりですが、Markdown書類のオープン時や編集中、PDF出力時などは遅くて辟易しました。画像がローカルにあったほうが速度的なメリットが大きいと判断し、変更しました。

AppleScript名:選択した画像を画像フォルダにコピーして、Markdown書式で画像パスタグをクリップボードに転送 v2
– Created 2017-11-09 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use mdLib : script “Metadata Lib” version “1.0.0″
–http://piyocast.com/as/archives/4968

property NSOrderedSame : a reference to current application’s NSOrderedSame
property NSFileManager : a reference to current application’s NSFileManager
property NSString : a reference to current application’s NSString

set imgPath to choose file of type {“public.image”}
set imgPOSIX to NSString’s stringWithString:(POSIX path of imgPath)
set imgFileName to (imgPOSIX’s lastPathComponent()) as string

–Get Front Document Path
tell application “MacDown”
  set dList to every document
  
if length of dList = 0 then
    display dialog “No Documents…” buttons {“OK”} default button 1
    
return
  end if
  
  
tell front document
    set aProp to properties
  end tell
  
  
set aPOSIXpath to POSIX path of ((file of aProp) as alias)
end tell

–Calculate Parent Book Folder Path
set aStr to NSString’s stringWithString:aPOSIXpath
set bStr to aStr’s stringByDeletingLastPathComponent()
set aList to bStr’s pathComponents() as list
–> {”/”, “Users”, “me”, “Documents”, “ぴよまるソフトウェア”, “書籍執筆関連、技術書典など”, “書籍原稿”, “–Book 1「AppleScript最新リファレンス」”, “5000 iOSデバイスとの連携”}

set bList to reverse of aList
–> {”5000 iOSデバイスとの連携”, “–Book 1「AppleScript最新リファレンス」”, “書籍原稿”, “書籍執筆関連、技術書典など”, “ぴよまるソフトウェア”, “Documents”, “me”, “Users”, “/”}

–Find Book Folder Root which name begins with “–”
set aCount to 1
set hitF to false
repeat with i in bList
  set j to contents of i
  
if j begins with “–” then –”–”ではじまる親フォルダを書籍のルートフォルダとみなす
    set hitF to true
    
exit repeat
  end if
  
set aCount to aCount + 1
end repeat

if hitF = false then
  display dialog “Folder structure error” buttons {“OK”} default button 1 with icon 1
  
return
end if

set cList to items 1 thru ((length of aList) - aCount + 1) of aList
set cStr to (NSString’s pathWithComponents:cList) as string

–Find Image folder in Book root folder
set theFolders to mdLib’s searchFolders:{cStr} searchString:“kMDItemContentType == [c]%@ && kMDItemFSName contains [c]%@” searchArgs:{“public.folder”, “_images”}

set theTargFol to contents of first item of theFolders
–> “/Users/me/Documents/ぴよまるソフトウェア/書籍執筆関連、技術書典など/書籍原稿/–Book 1「AppleScript最新リファレンス」/9999_images”

set newPath to ((NSString’s stringWithString:theTargFol)’s stringByAppendingPathComponent:imgFileName) as string
set newPath2 to chkExistPOSIXpathAndIncrementChildNumber(newPath) of me

–ファイルコピー
set aRes to my copyFileAt:imgPOSIX toFilePath:newPath2

–Markdown書類とコピーした画像ファイルの相対パスを計算する
set relativePath to calcRelativePath(aPOSIXpath, newPath2) of me

set allText to “![” & imgFileName & “](” & relativePath & “)”
–> “![img-2.php.jpeg](../9999_images/img-2.php.jpeg)”

set the clipboard to allText

–POSIX path stringを与えると、ファイル名の重複を検出して、ファイル名の名称回避を行って、ファイル名のみを返す
on chkExistPOSIXpathAndIncrementChildNumber(a)
  set aStr to NSString’s stringWithString:a
  
set bStr to aStr’s lastPathComponent()
  
set cStr to (bStr’s pathExtension()) as string
  
set dStr to (bStr’s stringByDeletingPathExtension()) as string
  
set eStr to (aStr’s stringByDeletingLastPathComponent()) as string
  
  
set aManager to NSFileManager’s defaultManager()
  
set aRes to (aManager’s fileExistsAtPath:aStr) as boolean
  
if aRes = false then return a
  
  
set hitF to false
  
repeat with i from 1 to 65535
    set tmpPath to (eStr & “/” & dStr & “_” & (i as string) & “.” & cStr)
    
set tmpStr to (NSString’s stringWithString:tmpPath)
    
set aRes to (aManager’s fileExistsAtPath:tmpStr) as boolean
    
set bRes to ((tmpStr’s caseInsensitiveCompare:eStr) is not equal to (NSOrderedSame)) as boolean
    
    
if {aRes, bRes} = {false, true} then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
  
if hitF = false then return false
  
  
–ファイルパス(フルパス)からファイル名部分を取得
  
set returnFileName to tmpStr’s lastPathComponent()
  
return (returnFileName as string)
  
end chkExistPOSIXpathAndIncrementChildNumber

–ファイルコピー
on copyFileAt:origPOSIXPath toFilePath:newPOSIXPath
  set POSIXPath1 to NSString’s stringWithString:origPOSIXPath
  
set POSIXPath2 to NSString’s stringWithString:newPOSIXPath
  
set fileManager to NSFileManager’s defaultManager()
  
set theResult to fileManager’s copyItemAtPath:POSIXPath1 toPath:POSIXPath2 |error|:(missing value)
  
return (theResult as integer = 1)
end copyFileAt:toFilePath:

–2つのPOSIX pathの相対パスを計算する
on calcRelativePath(aPOSIXfile, bPOSIXfile)
  set aStr to NSString’s stringWithString:aPOSIXfile
  
set bStr to NSString’s stringWithString:bPOSIXfile
  
  
set aList to aStr’s pathComponents() as list
  
set bList to bStr’s pathComponents() as list
  
  
set aLen to length of aList
  
set bLen to length of bList
  
  
if aLen bLen then
    copy aLen to aMax
  else
    copy bLen to aMax
  end if
  
  
repeat with i from 1 to aMax
    set aTmp to contents of item i of aList
    
set bTmp to contents of item i of bList
    
    
if aTmp is not equal to bTmp then
      exit repeat
    end if
  end repeat
  
  
set bbList to items i thru -1 of bList
  
set aaItem to (length of aList) - i
  
  
set tmpStr to {}
  
repeat with ii from 1 to aaItem
    set the end of tmpStr to “..”
  end repeat
  
  
set allRes to NSString’s pathWithComponents:(tmpStr & bbList)
  
return allRes as text
end calcRelativePath

★Click Here to Open This Script 

2017/11/09 指定フォルダ内のOSAXのSDEFファイルを指定フォルダに書き出す

指定フォルダ内のOSAX(Scripting Additions)のAppleScript用語辞書(sdef)のファイルを指定フォルダに書き出すAppleScriptです。

大昔のClassic Mac OS時代の(68kのバイナリが入っている)OSAXのAppleScript用語辞書の内容を最新のmacOS環境でファイルとして書き出せる、というなかなかに「お前以外に誰が使うんだ?」という内容ですが、割とトンでもない破壊力のあるものです。

sdef1.png

Classic Mac OS時代のScripting Additionsを資料としてとってありますが、これらのAppleScript用語辞書はaeteリソース内に書かれていました(この画面↓はClassic Mac OSエミュレータの「SheepShaver」で起動した漢字Talk 8.6上のスクリプトエディタ。ResEditを探したものの仮想マシン内にみつかりませんでした)。

classic_osax_resized.png

これらの用語辞書は現行のmacOSのスクリプトエディタでもオープンして内容を確認できます。

sdef4_resized.png

さらに、用語辞書のウィンドウ上部のプロキシーアイコンをCommand-クリックすると、テンポラリディレクトリ内に用語辞書(sdef)が書き出されていることを確認できます。

sdef5_resized.png

ということは、この用語辞書(sdef)のパスを求めると、コピーして保管しておくことが可能だということがわかります。

実行してOSAXの入っているフォルダ、書き出しフォルダを指定すると、

sdef2.png

sdefを書き出してくれます。

sdef3_resized.png

ResEdit(死語)でオープンしなくても、sdefなら単なるテキストなので、内容を参照することもかんたんです。

1回実行したら2度目はなさそうな内容なので、ほとんど書き捨てな内容です。途中、スクリプト用語辞書を参照できないOSAXもあったりで、オープン時にエラーダイアログが表示されたりしますが、そこは自分でOKボタンをクリックして回避する必要があります。

AppleScript名:指定フォルダ内のOSAXのSDEFファイルを指定フォルダに書き出す
– Created 2017-11-09 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4961

set a to choose folder with prompt “Original Classic OSAX folder”
set aExportFol to choose folder with prompt “SDEF export folder”

tell application “Finder”
  set aList to (every file of a) as alias list
end tell

repeat with i in aList
  set aRes to saveOSAXsdef(contents of i, aExportFol) of me
  
if aRes = false then
    log i
  end if
end repeat

on saveOSAXsdef(a, aExportFol)
  set aInfo to info for a
  
set aKind to kind of aInfo
  
  
if aKind is in {“スクリプティング機能追加”, “Scripting addition”} then –Scripting Addirions (*Localized*)
    try
      tell application “Script Editor”
        try
          set aDoc to open a
        on error
          return
        end try
        
        
tell front document
          set aPath to path
        end tell
      end tell
    on error
      return false
    end try
    
    
set aExpPOS to (POSIX path of aExportFol)
    
    
–ファイルをコピー
    
set aRes to my copyFileAt:aPath toFolder:aExpPOS
  else
    return false
  end if
  
  
tell application “Script Editor”
    close every document without saving
  end tell
  
return true
end saveOSAXsdef

on copyFileAt:POSIXPath toFolder:folderPath
  set POSIXPath to current application’s NSString’s stringWithString:POSIXPath
  
set folderPOSIXPath to current application’s NSString’s stringWithString:folderPath
  
– build path for new file
  
set theName to POSIXPath’s lastPathComponent()
  
set newPath to folderPOSIXPath’s stringByAppendingPathComponent:theName
  
set fileManager to current application’s 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 

2017/11/08 システム環境設定で指定のpane中の指定のanchorを表示する v2

システム環境設定で指定のpane中の指定のanchorを表示するAppleScriptです。

comapplesystempreferences_resized.png

システム環境設定はScriptableなアプリケーションですが、どのpane、どのpane中のanchorを表示するぐらいの機能しかついていません。項目によってはSystem Events経由で設定内容を取得・再設定することもできます。

syspref1_resized.png
▲「一般」「デスクトップとスクリーンセーバ」などの各項目がpane

syspref2_resized.png
▲「ディスプレイ」「配置」などの各項目がanchor。ただし、かならずしも各タブ項目がanchorとして定義されているわけではない

Mac OS X 10.2:ただ辞書がついただけ
Mac OS X 10.4:paneとanchorが取得できるようになった。revealコマンドが追加された
Mac OS X 10.5:print系の機能が強化された

と、Mac OS X 10.4でほぼ機能が確立して、以後あまりAppleScriptから利用できる機能について変更されていません。最新のSystem Preferences.app v14ではauthorizeというユーザー認証(ユーザー名とパスワードの入力)を求める機能が追加されたぐらいです。

syspref_auth.png

authorizeでユーザー認証が成功したかどうか、という結果は返ってきません。ただ、認証のダイアログを表示させるだけのコマンドです。

ただし、システム環境設定のPaneにどのようなものがあり、その中に含まれるanchorにどのようなものがあるかどうかは、OSバージョンごとに異なります。

AppleScript名:システム環境設定で指定のpane中の指定のanchorを表示する v2
– Created 2017-10-10 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4956

set aList to getEveryPaneID() of me
–> “com.apple.preferences.appstore”, “com.apple.preferences.Bluetooth”, “com.nvidia.CUDAPref”….}

set pList to getEveryAnchorName(“com.apple.preference.displays”) of me
–> {”displaysArrangementTab”, “displaysNightShiftTab”, “displaysColorTab”, “displaysGeometryTab”, “displaysDisplayTab”}

set aRes to dispSystemPreferences(“com.apple.preference.displays”, “displaysNightShiftTab”) of me
–> true

on dispSystemPreferences(paneID, anchorID)
  tell application “System Preferences”
    activate
    
set pList to id of every pane
    
if paneID is not in pList then return false
    
    
set current pane to pane paneID
    
tell current pane
      set nList to name of every anchor
      
if anchorID is not in nList then return false
      
reveal anchor anchorID
    end tell
  end tell
  
  
return true
end dispSystemPreferences

on getEveryPaneID()
  tell application “System Preferences”
    return id of every pane
  end tell
end getEveryPaneID

on getEveryAnchorName(paneID)
  tell application “System Preferences”
    set pList to id of every pane
    
if paneID is not in pList then return false
    
set current pane to pane paneID
    
tell current pane
      return name of every anchor
    end tell
  end tell
end getEveryAnchorName

★Click Here to Open This Script 

AppleScript名:システム環境設定の「セキュリティとプライバシー」でユーザー認証
– Created 2017-10-10 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4956

set aRes to dispSystemPreferences(“com.apple.preference.security”, “Privacy_Assistive”) of me

tell application “System Preferences”
  authorize current pane
end tell

on dispSystemPreferences(paneID, anchorID)
  tell application “System Preferences”
    activate
    
set pList to id of every pane
    
if paneID is not in pList then return false
    
    
set current pane to pane paneID
    
tell current pane
      set nList to name of every anchor
      
if anchorID is not in nList then return false
      
reveal anchor anchorID
    end tell
  end tell
  
  
return true
end dispSystemPreferences

★Click Here to Open This Script 

2017/11/07 1D Listから重複項目のみ抽出 v2

1D List(1次元配列)中における重複項目のみをリストアップするAppleScriptです。

Pure AppleScriptでもよく書く内容ですが(書き捨てレベル)、Cocoaの機能を利用するとこんな感じでしょうか。Predicates文で表記できそうな気もするのですが、、、、

AppleScript名:1D Listから重複項目のみ抽出 v2
– Created 2017-11-07 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4955

property NSCountedSet : a reference to current application’s NSCountedSet

set aList to {1, 2, 3, 4, 1, 2, 3, 4, 5, 1, 2, 3, 1, 2, 3, 8, 10, -2}
set aRes to returnDuplicatesOnly(aList) of me
–>  {1, 2, 3, 4}

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

★Click Here to Open This Script