Archive for 9月, 2017

2017/09/30 VPN接続、接続解除

システム環境設定の「ネットワーク」で定義しておいたVPN接続サービスに対して接続、接続解除を行うAppleScriptです。

vpn1.png

VPN接続サービスの定義自体はAppleScriptから新規定義することはできません(無理やり、GUI Scripting経由でやればできなくはありませんが)。システム環境設定の「ネットワーク」であらかじめ作成しておくことになります。

VPN接続についてはメニューバー上に接続メニューを出しておけますが、メニューから操作するのとほぼ同様の機能を実行します。

vpn2.png

とりあえず、各ネットワークサービスがどのような設定値を持っているのか、持っていないのかを調べておきます。

vpn0.png

スクリプトエディタ上でログ表示状態にしておいて、スクリプトエディタ上でAppleScriptを実行すれば、スクリプトエディタ上に実行ログが表示されます。このあたり、別のプログラム(Folder Actionなど)で実行中のログまでは表示されません。

AppleScript名:Network Interfaceの調査
– Created 2017-09-30 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4860

tell application “System Events”
  tell current location of network preferences
    set sList to every service
    
repeat with i in sList
      set aP to properties of i
      
set aInt to interface of aP
      
try
        set bP to speed of aInt
        
log bP
      end try
    end repeat
  end tell
end tell

★Click Here to Open This Script 

ここで、各ネットワークサービスの種別が番号で取得できて、VPNを定義しておいたネットワークサービスが13番を返してきたので、種別が13のものをVPNと見立てて、接続および接続解除を行います。

存在しているVPNサービスを調査して、該当するものを取得する、というのは実にAppleScriptらしい処理です。ここで、単にサービス名称を文字列で指定してconnect/disconnectするというScriptだと、まったくAppleScriptのメリットを活かせないうえに、環境が変わると当然VPN名称も変わってくるので、実行できないことになってしまいます。

VPN接続自体をScriptから制御することに意味があるかどうかという話はありますが、未接続かどうかをチェックし、接続していなかったら再接続を行い、VPN接続先のMacのドライブをマウントしたり、vncによる画面共有を開始したり、リモート先で起動しているAppleScriptアプレットにコマンドを送ったりという運用ができることになります。作って実際に動かしてみたら、「十分に意義はある」と感じられました。

AppleScript名:VPN経由でネットワーク接続
– Created 2017-09-30 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4860

tell application “System Events”
  tell current location of network preferences
    set sList to every service whose kind is 13 –種別がVPNのNetwork Service
    
if sList = {} then return false
    
    
if length of sList is not equal to 1 then
      –複数VPNが存在している場合にはユーザー選択
      
set ssRes to choose from list sList
      
if ssRes = false then return false
      
set sRes to first item of ssRes
    else
      set sRes to first item of sList
    end if
    
    
connect sRes
  end tell
end tell

★Click Here to Open This Script 

AppleScript名:VPN経由のネットワーク接続を切断する
– Created 2017-09-30 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4860

tell application “System Events”
  tell current location of network preferences
    set sList to every service whose kind is 13 and active is true –種別がVPNのNetwork Serviceで状態がactive(接続中)のものを取得
    
if sList = {} then return false
    
    
if length of sList is not equal to 1 then
      –複数VPNが存在している場合にはユーザー選択
      
set ssRes to choose from list sList
      
if ssRes = false then return false
      
set sRes to first item of ssRes
    else
      set sRes to first item of sList
    end if
    
    
disconnect sRes
  end tell
end tell

★Click Here to Open This Script 

しかし、System EventsまわりはAppleScript用語辞書に書かれていても実際には値を返してこない項目(speedとか)のオンパレードなので、Appleにはちゃんと仕事をしてほしいところです。

2017/09/29 macOS 10.13で変更されたPDFアノテーションの作成

macOS 10.13, High Sierraで変更になった、PDF書類へのアノテーション作成を行うAppleScriptです。

とくにサンプルプログラムが見当たらなかったのですが、「まあこんなもんだろー」とあたりをつけて書いてみたら動きました。Objective-CのコードもSwiftのコードも見当たらず、いまのところPDFアノテーション作成コードはこのAppleScriptしか見当たらない状態、、、、、、

選択したPDFに対してアノテーションを追加します。自分は動作検証用に作っためもり入りの「方眼紙PDF」に対してアノテーションを追加してみました(掲載スクリーンショット)。PDFの座標系は左下が原点です。

AppleがmacOS 10.13に作ったScripting BridgeのNSRectの変換ミス(?)のバグに対処してあります。恥かしいレベルのバグだし、まいどまいど腹がたつのでさっさと修正してほしいのですが、いつごろ修正されるものやら。予想以上に今回のバグの被害範囲が広いので、さっさと修正してほしいものです(指定写真の顔の部分をエリア検出してフィルタをかけるといった処理が途中でエラーに。ものすごく腹立たしい)。

circle_annotation_resized.png

従来の書き方でもまだ動くので、今日明日ですぐに切り替えが必要になるわけではなさそうです。

新しい書き方のほうが単純で、さまざまなアノテーションでもだいたい同様に記述できるので、個人的にはこちらのほうがいい感じがします。

→ 指定PDFの最初のページに大量のスクウェアアノテーションを添付する

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

AppleScript名:指定PDFの最初のページにサークルアノテーションを追加する(High Sierra)
– Created 2017-09-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.7″ –macOS 10.13 or later
use scripting additions
use framework “Foundation”
use framework “Quartz”
use framework “AppKit”
–http://piyocast.com/as/archives/4855

set aHFSPath to (choose file of type {“com.adobe.pdf”} with prompt “Choose a PDF”)
set aPOSIX to POSIX path of aHFSPath
set aURL to (current application’s |NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()
set firstPage to (aPDFdoc’s pageAtIndex:0)

–macOS 10.13以降で指定する形式のAnnotation
set keyList to {current application’s PDFAnnotationKeyColor, current application’s PDFAnnotationKeyInteriorColor}
set valList to {current application’s NSColor’s blackColor(), current application’s NSColor’s grayColor()}
set propDict to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList

–macOS 10.13のバグ対策。NSRectはwidth–>heightの順にパラメータを並べる
set anAnnotation to current application’s PDFAnnotation’s alloc()’s initWithBounds:{origin:{x:100, y:100}, |size|:{width:600, height:600}} forType:(current application’s PDFAnnotationSubtypeCircle) withProperties:propDict

firstPage’s addAnnotation:anAnnotation

aPDFdoc’s writeToFile:aPOSIX

★Click Here to Open This Script 

square_annotation_resized.png

AppleScript名:指定PDFの最初のページにスクウェアアノテーションを追加する(High Sierra)
– Created 2017-09-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.7″ –macOS 10.13 or later
use scripting additions
use framework “Foundation”
use framework “Quartz”
use framework “AppKit”
–http://piyocast.com/as/archives/4855

set aHFSPath to (choose file of type {“com.adobe.pdf”} with prompt “Choose a PDF”)
set aPOSIX to POSIX path of aHFSPath
set aURL to (current application’s |NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()
set firstPage to (aPDFdoc’s pageAtIndex:0)

–macOS 10.13以降で指定する形式のAnnotation
set keyList to {current application’s PDFAnnotationKeyColor, current application’s PDFAnnotationKeyInteriorColor}
set valList to {current application’s NSColor’s blackColor(), current application’s NSColor’s grayColor()}
set propDict to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList

–macOS 10.13のバグ対策。NSRectはwidth–>heightの順にパラメータを並べる
set anAnnotation to current application’s PDFAnnotation’s alloc()’s initWithBounds:{origin:{x:100, y:100}, |size|:{width:500, height:500}} forType:(current application’s PDFAnnotationSubtypeSquare) withProperties:propDict

firstPage’s addAnnotation:anAnnotation

aPDFdoc’s writeToFile:aPOSIX

★Click Here to Open This Script 

text_annotation_resized.png

AppleScript名:指定PDFの最初のページにテキストアノテーションを追加する(High Sierra)
– Created 2017-09-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.7″ –macOS 10.13 or later
use scripting additions
use framework “Foundation”
use framework “Quartz”
use framework “AppKit”
–http://piyocast.com/as/archives/4855

set aHFSPath to (choose file of type {“com.adobe.pdf”} with prompt “Choose a PDF”)
set aPOSIX to POSIX path of aHFSPath
set aURL to (current application’s |NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()
set firstPage to (aPDFdoc’s pageAtIndex:0)

–macOS 10.13以降で指定する形式のAnnotation
set keyList to {current application’s PDFAnnotationKeyInteriorColor, current application’s PDFAnnotationKeyContents}
set valList to {current application’s NSColor’s yellowColor(), “Piyomaru Software” & return & “ぴよまるソフトウェア”}
set propDict to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList

–macOS 10.13のバグ対策。NSRectはwidth–>heightの順にパラメータを並べる
set anAnnotation to current application’s PDFAnnotation’s alloc()’s initWithBounds:{origin:{x:100, y:100}, |size|:{width:200, height:50}} forType:(current application’s PDFAnnotationSubtypeFreeText) withProperties:propDict

firstPage’s addAnnotation:anAnnotation

aPDFdoc’s writeToFile:aPOSIX

★Click Here to Open This Script 

line_annotation_resized.png

AppleScript名:指定PDFの最初のページにラインアノテーションを追加する(High Sierra)
– Created 2017-09-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.7″ –macOS 10.13 or later
use scripting additions
use framework “Foundation”
use framework “Quartz”
use framework “AppKit”
–http://piyocast.com/as/archives/4855

set aHFSPath to (choose file of type {“com.adobe.pdf”} with prompt “Choose a PDF”)
set aPOSIX to POSIX path of aHFSPath
set aURL to (current application’s |NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()
set firstPage to (aPDFdoc’s pageAtIndex:0)

–macOS 10.13以降で指定する形式のAnnotation
set keyList to {current application’s PDFAnnotationKeyColor}
set valList to {current application’s NSColor’s blackColor()}
set propDict to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList

–macOS 10.13のバグ対策。NSRectはwidth–>heightの順にパラメータを並べる
set anAnnotation to current application’s PDFAnnotation’s alloc()’s initWithBounds:{origin:{x:100, y:100}, |size|:{width:600, height:600}} forType:(current application’s PDFAnnotationSubtypeLine) withProperties:propDict

firstPage’s addAnnotation:anAnnotation

aPDFdoc’s writeToFile:aPOSIX

★Click Here to Open This Script 

2017/09/27 macOS 10.13であらたにサポートされたHEIF画像をJPEGに変換

macOS 10.13であらたにサポートされたHEIF形式の画像をJPEGに変換するAppleScriptです。

HEIF(High Efficiency Image Format)は高品質の画像を少ないファイル容量で保持できる画像フォーマット。macOS 10.13およびiOS 11からサポートされるようになりました。拡張子は「heif」あるいは「heic」とのこと。

実際にHEIFのサンプル画像をJPEGに圧縮率を変更しつつ書き出してみたところ、60%ぐらいの圧縮率のJPEGファイルとHEIF画像が同程度のファイルサイズ(素材によるんでしょうけれど)。非圧縮のJPEG(1.6Mバイト)に対してHEIFは340Kバイト。見た目は非圧縮のJPEGと区別できませんでした。

heif_preview.png

HEIF形式の画像ファイルをFinder上でダブルクリックすると、Preview.appでなにごともなく表示されます。ただし、JPEGなどの画像をHEIFで書き出す機能はPreview.appにはついておらず、他の環境でサポートしていないHEIF画像をそのまま外に出してほしくない、といったところでしょうか。

heif_preview2.png

とりあえず、Cocoaを経由してHEIF画像をAppleScriptでハンドリングできないかと調べてみたところ、普通に(何も変更なく)NSImageに読み込んで、JPEGで書き出せました。

その逆に、JPEG画像をHEIF形式に変換する方法は、調べてもあまり素直な方法(Cocoaのオブジェクトを呼び出して書き出し)は見つかりません。無理やりBrew経由でコマンドラインツールをインストールして変換という状況。

それもそのはずで、HEIFは画像フォーマットというよりもコンテナであり、その中にはさまざまなデータ(画像、派生画像、イメージシーケンス、補助画像アイテム、メタデータ)を格納できるようになっているとのこと。

一応、AVFoundationに画像形式「AVFileTypeHEIF」の定義があるようです。

macOS 10.14〜10.15ぐらいでOS内にきちんとした仕組みを用意しようというところなんでしょうか。

AppleScript名:指定HEIF画像をJPG形式で保存
– Created 2017-09-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.7 –10.13 or later
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4851

set aFile to POSIX path of (choose file of type {“public.heic”} with prompt “Select HEIF image file”)
set aImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile

set fRes to retUUIDfilePath(aFile, “jpg”) of me
set sRes to saveNSImageAtPathAsJPG(aImage, fRes, 0.6) of me

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

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

★Click Here to Open This Script 

2017/09/27 書式つきテキストを組み立ててサイズを取得して画像書き出し v3a

複数行にわたる書式付きテキストを組み立てて、描画サイズを取得して画像書き出しするAppleScriptのmacOS 10.13のバグ回避版です。

前バージョンをmacOS 10.13で実行したところ、下地を塗る処理が正しく実行されず、首をひねっていたのですが・・・Shane Stanleyが原因を「macOS 10.13で新たに作られたバグ」であることをつきとめました(見つけるShaneもすごいですが、Appleの開発チームの非力さを呪う)。

原点座標(origin)と大きさ(size)を指定するNSRectが、macOS 10.13では属性ラベルが外れた入れ子のリストとして(Scripting Bridgeで)解釈されることに問題があるようです。

つまり、以下のコード、

use framework “Foundation”
use framework “AppKit”
set theFrame to current application’s NSScreen’s mainScreen()’s visibleFrame()
–> {origin:{x:0.0, y:0.0}, |size|:{width:2524.0, height:1417.0}}

★Click Here to Open This Script 

をmacOS 10.12までの環境で実行すると、

–> {origin:{x:0.0, y:0.0}, |size|:{width:2524.0, height:1417.0}}

と返ってくるのですが、macOS 10.13で実行すると、

–> {{0.0, 0.0}, {2524.0, 1417.0}}

となってしまう、と。

これでも一応動いているところはすごいですが、この、

{width:2524.0, height:1417.0}

のところで、widthとheightの記述順が、

{height:1417.0, width:2524.0}

だと、macOS 10.13ではまことに馬鹿馬鹿しいことにDictionaryのはずのものがArrayとして認識されるため、属性値ラベルをつけて書いていても記述順が変わると正しく動かないとのこと(よくShaneこれ見つけたなー ^ー^;)。本バグはShaneによりAppleのBug Reporterに報告されているそうです。

なので、本ScriptでもNSRectの記述時のwidthとheightの記述順を入れ替えました(汗) それだけだとバカすぎるので、日本語の縦書きが有効かどうかもテストしておきました。

test9999_3.png

AppleScript名:書式つきテキストを組み立ててサイズを取得して画像書き出し v3a
– Created 2017-09-25 by Takaaki Naganoya
– Modified 2017-09-27 by Shane Stanley
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4850

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

set outPath to “~/Desktop/test9999.png”
set fillColor to NSColor’s whiteColor –塗り色
set aFontSize to 48
set aString to “Takaaki Naganoya” & return & “長野谷隆昌”

set anAssrStr to makeRTFfromParameters(aString, “HiraMinProN-W3″, aFontSize, -2, (aFontSize * 1.5)) of me
set aSize to anAssrStr’s |size|()
log aSize
–>  {width:387.424, height:144.0}

set attrStrWidth to width of aSize
set attrStrHeight to height of aSize

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

–PNG形式でファイルに保存
set aRes to saveImageRepAtPathAsPNG(tmpImg2, outPath) of me

–画像のうえに指定のスタイル付きテキストを描画して画像を返す
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)
  –Font
  
set aVal1 to NSFont’s fontWithName:fontName |size|:aFontSize
  
set aKey1 to (NSFontAttributeName)
  
  
–Color
  
set aVal2 to NSColor’s blackColor()
  
set aKey2 to (NSForegroundColorAttributeName)
  
  
–Kerning
  
set aVal3 to aKerning
  
set akey3 to (NSKernAttributeName)
  
  
–Underline
  
set aVal4 to 0
  
set akey4 to (NSUnderlineStyleAttributeName)
  
  
–Ligature
  
set aVal5 to 2 –all ligature ON
  
set akey5 to (NSLigatureAttributeName)
  
  
–縦書き指定–Japanese tategaki setting (Up to down, right to left direction drawing)
  
set aVal6 to true
  
set aKey6 to (current application’s NSVerticalGlyphFormAttributeName)
  
  
–Paragraph space
  
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, aKey6, akey7}
  
set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aVal6, 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}} –AppleがmacOS 10.13に作ったバグのせいで、width→heightの順番にパラメータを書かないと異常動作する
  
set theNSBezierPath to NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  
fillColor’s |set|()
  
theNSBezierPath’s fill()
  
anImage’s unlockFocus()
  
  
return anImage
end makeImageWithFilledColor

–画像を指定パスにPNG形式で保存
on saveImageRepAtPathAsPNG(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
end saveImageRepAtPathAsPNG

★Click Here to Open This Script 

2017/09/26 macOS 10.13, High Sierraリリース

まだメイン環境には入れていませんが、サブマシンにはインストールしたmacOS 10.13, High Sierra。GMから正式版へのアップデートもそれなりに時間がかかりました。

あまりバグを修正することに熱心でない様子で(7月ぐらいからバグ報告しても何も反応しなくなった)、巨大なバグを放置してでも通常の10月末のリリース時期を繰り上げたのは、何か9〜10月に「隠し球」があるためでしょうか。

High SierraのOS自体には何も驚きがありませんが、写真.app(Photos.app)v3で写真を検索するのに汎用的なキーワードを(タグ付けしていないのに)受け付けたのには驚かされました。それほどこまかいキーワードが用意されているわけではなさそうですが、「ラーメン」「カフェ」「屋外」「海」ぐらいのキーワードは受け付けるようです。

photos_tag.png

これをローカルに機械学習モデルを持たせて実現しているとしたら、モデルデータのサイズはかなり巨大なものになりそうですが、どうもこれはサーバー側(Web API)で行なっている気配がします(どこにもそのような記述はありませんが)。

2017/09/26 バンドルIDで指定したプロセスを強制終了

Bundle IDで指定したプロセスを強制終了するAppleScriptです。

実際に試してみたらshellのkill/killallコマンドなどと異なり、Finderを強制終了したら終了しっぱなしになりました。

FinderをkillしてFinderがわりのアプリケーションを起動しておく用途のために必要な機能です。

AppleScript名:バンドルIDで指定したプロセスを強制終了(NSRunningApplication)
– Created 2017-09-17 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/4847

set pRes to forceQuitAProcessByBUndleID(“com.apple.finder”) of me

–指定プロセスの強制終了
on forceQuitAProcessByBUndleID(aBundleID)
  set appArray to current application’s NSRunningApplication’s runningApplicationsWithBundleIdentifier:aBundleID
  
if appArray’s |count|() > 0 then
    set appItem to appArray’s objectAtIndex:0
    
set aRes to (appItem’s terminate()) as boolean
    
return aRes
  else
    return false
  end if
end forceQuitAProcessByBUndleID

★Click Here to Open This Script 

2017/09/25 書式つきテキストを組み立ててサイズを取得して画像書き出し v2

複数行にわたる書式付きテキストを組み立てて、描画サイズを取得して画像書き出しするAppleScriptです。

スタイル付きテキスト(NSAttributedString)を作成し、その描画エリアのサイズを取得。そんなもん、こんな手軽に求められるとは思ってもみませんでした。

描画エリアのサイズをもとに空白画像を作成して、そこにスタイル付きテキストを描画。結果の画像を指定パス名で書き出します。

test9999.png
▲本Scriptの実行結果(macOS 10.12)。出力用の文字列を長くしても、描画サイズを調べて画像化しているので、途中で途切れたりはしません

ただし、意外なところでOSバージョンによる挙動の差が出ました。書式付きテキストの描画サイズの取得ではなく、下地の塗りつぶし(白)についてです(下図参照)。

1013_strange_behavior.png
▲macOS 10.12(上)とmacOS 10.13GM(下)の本Scriptの実行結果。検証のために文字データを変更(途中の改行を減らしている)

macOS 10.13のRelease Build(Build 17A365)でも同様の結果になりました。macOS 10.10で実験したら10.12と同様の結果に。何か(トンでもない場所の)仕様が変わったのだろうか?

→ macOS 10.13のバグでした。対応版を掲載しています

AppleScript名:書式つきテキストを組み立ててサイズを取得して画像書き出し v2
– Created 2017-09-25 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 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

set outPath to “~/Desktop/test9999.png”
set fillColor to NSColor’s whiteColor –塗り色
set aFontSize to 48
set aString to “Takaaki” & return & “Naganoya” & return & “長野谷” & return & “隆昌”

set anAssrStr to makeRTFfromParameters(aString, “HiraMinProN-W3″, aFontSize, -2, (aFontSize * 1.5)) of me
set aSize to anAssrStr’s |size|()
–>  {width:408.0, height:187.2}

set attrStrWidth to width of aSize
set attrStrHeight to height of aSize

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

–PNG形式でファイルに保存
set aRes to saveImageRepAtPathAsPNG(tmpImg2, outPath) of me

–画像のうえに指定のスタイル付きテキストを描画して画像を返す
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)
  –Font
  
set aVal1 to NSFont’s fontWithName:fontName |size|:aFontSize
  
set aKey1 to (NSFontAttributeName)
  
  
–Color
  
set aVal2 to NSColor’s blackColor()
  
set aKey2 to (NSForegroundColorAttributeName)
  
  
–Kerning
  
set aVal3 to aKerning
  
set akey3 to (NSKernAttributeName)
  
  
–Underline
  
set aVal4 to 0
  
set akey4 to (NSUnderlineStyleAttributeName)
  
  
–Ligature
  
set aVal5 to 2 –all ligature ON
  
set akey5 to (NSLigatureAttributeName)
  
  
–Paragraph space
  
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}, {height:aHeight, width:aWidth}}
  
set theNSBezierPath to NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  
fillColor’s |set|()
  
theNSBezierPath’s fill()
  
anImage’s unlockFocus()
  
  
return anImage
end makeImageWithFilledColor

–画像を指定パスにPNG形式で保存
on saveImageRepAtPathAsPNG(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
end saveImageRepAtPathAsPNG

★Click Here to Open This Script 

2017/09/24 迷路をRTFで作成して脱出経路を赤く着色する v3

2D迷路の作成プログラム「MazeFinder」(By Toby Jennings)をフレームワーク化したものを呼び出して、文字ベースの迷路データをRTFに変換して脱出経路情報を着色してデスクトップに書き出すAppleScriptのアップデート版です。

迷路データの作成はこの際どうでもよく、文字列の検索を行う部分で問題が起こったことへの対処を行いました。

NSStringの文字検索メソッドrangeOfString: で、検索文字列が見つからなかった場合にNSNotFound(=-1)が返ってくるはずですが、macOS 10.13 betaで動かしてみたところNSNotFoundではなく巨大な数が返ってくることに気づきました。9,223,372,036,854,775,807と巨大すぎてAppleScriptの処理系では数値の精度上限を超えてしまっています。

いろいろ情報収集してみたところ、10.13にかぎらずよく知られた動作のようで、-1ではなく巨大な数を返してしまうパターンがある、と認識しておく必要があるようです(いつものApple仕事)。

AppleScript名:迷路をRTFで作成して脱出経路を赤く着色する v3
– Created 2017-09-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “MazeFinder” –https://github.com/tcjennings/MazeFinder
–http://piyocast.com/as/archives/4842

property NSColor : a reference to current application’s NSColor
property Board2D : a reference to current application’s Board2D
property NSUUID : a reference to current application’s NSUUID
property NSDictionary : a reference to current application’s NSDictionary
property NSMutableArray : a reference to current application’s NSMutableArray
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSFont : a reference to current application’s NSFont
property NSString : a reference to current application’s NSString
property NSDocumentTypeDocumentAttribute : a reference to current application’s NSDocumentTypeDocumentAttribute
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName
property Pathfinder : a reference to current application’s Pathfinder
property NSLiteralSearch : a reference to current application’s NSLiteralSearch
–NSNotFoundはプロパティに代入しても認識されなかった

set targFontName to “Courier-Bold” –PostScript Name
set targFontSize to 13 –Point

–迷路テキストデータ作成→Attributed Stringに
set aStr to create2DMazeAndSolveIt(30, 30, 1, 1, 28, 28) of me –Plain Text
set anAttr to changeAttrStrsFontAttribute(aStr, targFontName, targFontSize) of me –Attributed String

–迷路データに着色(参照呼び出し, Call by reference)
markCharOfAttributedString(anAttr, aStr, “!”, (NSColor’s redColor())) of me

–結果のRTFをデスクトップ上に書き出す。ファイル名はUUID.rtf
set targFol to current application’s NSHomeDirectory()’s stringByAppendingPathComponent:“Desktop”
set aRes to my saveStyledTextAsRTF(targFol, anAttr)

–指定のAttributed String内で指定文字列が含まれる箇所に指定の色をつける(結果はメイン側に参照渡し)
on markCharOfAttributedString(anAttr, origStr, aTargStr, aColor)
  set rList to searchWordWithRange(origStr, aTargStr) of me
  
repeat with ii in rList
    (anAttr’s addAttribute:(NSForegroundColorAttributeName) value:aColor range:ii)
  end repeat
end markCharOfAttributedString

–指定の文字列をAttributed Stringに変換して任意のフォントを一括指定
on changeAttrStrsFontAttribute(aStr, aFontPSName, aFontSize)
  set tmpAttr to NSMutableAttributedString’s alloc()’s initWithString:aStr
  
set aRange to current application’s NSMakeRange(0, tmpAttr’s |length|())
  
set aVal1 to NSFont’s fontWithName:aFontPSName |size|:aFontSize
  
tmpAttr’s beginEditing()
  
tmpAttr’s addAttribute:(NSFontAttributeName) value:aVal1 range:aRange
  
tmpAttr’s endEditing()
  
return tmpAttr
end changeAttrStrsFontAttribute

–指定テキストデータ(atargText)内に、指定文字列(aSearchStr)が含まれる範囲情報(NSRange)をすべて取得する
on searchWordWithRange(aTargText, aSearchStr)
  set aStr to NSString’s stringWithString:aTargText
  
set bStr to NSString’s stringWithString:aSearchStr
  
set hitArray to NSMutableArray’s alloc()’s init()
  
set cNum to (aStr’s |length|()) as integer
  
  
set aRange to current application’s NSMakeRange(0, cNum)
  
  
repeat
    set detectedRange to aStr’s rangeOfString:bStr options:NSLiteralSearch range:aRange
    
set aLoc to (detectedRange’s location)
    
–CAUTION !!!! Sometimes aLoc returns not NSNotFound (-1) but a Very large number
    
if (aLoc > 9.999999999E+9) or (aLoc = (current application’s NSNotFound)) then exit repeat
    
    
hitArray’s addObject:detectedRange
    
    
set aNum to aLoc as integer
    
set bNum to (detectedRange’s |length|) as integer
    
    
set aRange to current application’s NSMakeRange(aNum + bNum, cNum - (aNum + bNum))
  end repeat
  
  
return hitArray
end searchWordWithRange

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsRTF(targFol, aStyledString)
  set bstyledLength to aStyledString’s |string|()’s |length|()
  
set bDict to NSDictionary’s dictionaryWithObject:“NSRTFTextDocumentType” forKey:(NSDocumentTypeDocumentAttribute)
  
set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict
  
  
set theName to (NSUUID’s UUID()’s UUIDString())
  
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

–2D迷路を作成して脱出経路を検索して文字列で迷路データを出力する
–迷路サイズ x,y スタート座標 x, y ゴール座標 x,y
on create2DMazeAndSolveIt(xMax, yMax, xStart, yStart, xGoal, yGoal)
  set myBoard to Board2D’s alloc()’s init()
  
myBoard’s setupBoardWithRows:xMax WithColumns:yMax –60×60ぐらいが上限。正方形でないとダメ
  
myBoard’s createMazeFromBoard()
  
  
set myPathfinder to Pathfinder’s alloc()’s init()
  
set myPath to myPathfinder’s findPathThroughMaze:myBoard fromX:xStart fromY:yStart toX:xGoal toY:yGoal
  
  
set aCount to myPath’s |count|()
  
if aCount > 0 then
    set aRes to myBoard’s drawPathThroughMaze:myPath
    
return aRes as string
  else
    return false
  end if
end create2DMazeAndSolveIt

★Click Here to Open This Script 

2017/09/23 指定日時以降に作成されたファイルをSpotlight検索 v3

指定フォルダ(Desktop)以下のファイルで、指定日時以降に作成されたものをSpotlight検索するAppleScriptの修正版です。

Metadata Libの作者であるShane Stanleyから直接「こうしたほうがいいよ」と教えてもらいました。たしかに、dateをイチイチISO8601日付文字列に変換しないといけないというのは、さすがにおかしいと思っていました(汗)。

記述と処理がシンプルになっただけで、v2でもv3でも処理時間に大差はありません。同じ対象に対して同じ検索を行なって、0.01秒弱というところでした。

本ルーチンでは指定年月日でNSDateオブジェクトを生成し、それをもとにMetadata LibでSpotlight検索を行なっています。実行のさいには、Metadata Libを~/Library/Script Libraries/フォルダに入れておく必要があります。

従来、自分はAppleのMailing ListやWebフォーラムなどの情報、および海外で販売されていたAppleScript CD-ROMなどを購入するなどして、ほぼ一方的に英語ベースの情報を受け身で収集していたわけですが、このようなBlogによる情報発信やらGoogle翻訳の精度向上の影響で、割と些細な情報も相互に伝わるようになりつつある昨今です。

AppleScript名:指定日時以降に作成されたファイルをSpotlight検索 v3
– 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/4841

property NSTimeZone : a reference to current application’s NSTimeZone
property NSCalendar : a reference to current application’s NSCalendar

set aDate to getDateInternationalYMDhms(2017, 9, 21, 23, 2, 0) of me
–>  (NSDate) 2017-09-21 14:02:00 +0000

set thePath to POSIX path of (path to desktop)
–>  ”/Users/me/Desktop/”

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

on getDateInternationalYMDhms(aYear, aMonth, aDay, anHour, aMinute, aSecond)
  set theNSCalendar to NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:anHour minute:aMinute |second|:aSecond nanosecond:0
  
return theDate
end getDateInternationalYMDhms

★Click Here to Open This Script 

2017/09/22 指定日時以降に作成されたファイルをSpotlight検索 v2

指定フォルダ(Desktop)以下のファイルで、指定日時以降に作成されたものをSpotlight検索するAppleScriptです。

本ルーチンの原型は、電子書籍を作成する際にMarkdown書類(MacDown)とPages書類をまとめてPDFに出力して1つのPDFに連結するAppleScriptで使っていたものです(こういう武器がないと、電子書籍を執筆からレイアウトから何から何までひとりで行うことなんてできません)。

bookfiles2.png
Pagesはともかく、MacDownからは指定フォルダにPDFを書き出すという機能は(MacDownのAppleScript用語辞書に機能が用意されていないため)AppleScriptからは利用できませんでした。そこで、GUI Scriptingで強引にデスクトップにPDF書き出しすることに。

デスクトップに書き出させたのは、保存ダイアログ上でCommand-Dのキーボードショートカットで一意に指定できる数少ないフォルダだからです(このあたり、生活の知恵)。

GUI Scriptingでファイル保存やファイル書き出しを行う場合、保存ダイアログがどこのフォルダを指し示すかはアプリケーション側まかせなので、保存/書き出し先フォルダをデスクトップに強引に指定する処理はたいへん重要です。GUI Scriptingはたいへんに弱点が多く不確実な手段ですが、不確実さを地道につぶしてあげることで、信頼性のある処理フローを構築できます。

PDF書き出し開始時のタイムスタンプを変数に記憶させておき、その時刻以降でデスクトップに生成されたPDFをすべて取得し、ファイル名でソート(ファイル名の先頭にソート用の仮想ノンブルをつけて管理)し、(末尾に生じていた意図しない空白ページ自動削除しつつ)1つのPDFに合成していました。

そのため、日時を指定して指定フォルダに存在するファイルをSpotlight検索する本ルーチンの重要度はとても高かったわけです。

当初は日付情報をJan 1 2001 00:00:00 GMTからの相対秒(Absolute Time)で指定していたのですが、OSのバージョンアップにともないこの書き方が使えなくなったため、仕方なく対処。

本ルーチンでは指定年月日でdateオブジェクトを生成したあと、ISO8601形式の日付文字列に変換し、それをもとにShane StanleyのMetadata LibでSpotlight検索を行なっています。実行のさいには、Metadata Libを~/Library/Script Libraries/フォルダに入れておく必要があります。

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

property NSTimeZone : a reference to current application’s NSTimeZone
property NSCalendar : a reference to current application’s NSCalendar
property NSDateFormatter : a reference to current application’s NSDateFormatter

set aDate to getDateInternationalYMDhms(2017, 9, 21, 23, 2, 0) of me
–>  date “2017年9月21日木曜日 23:02:00″

set bStr to retISO8601DateTimeString(aDate) as string
–>  ”2017-09-21T14:02:00Z”

set thePath to POSIX path of (path to desktop)

set theFiles to mdLib’s searchFolders:{thePath} searchString:(“kMDItemFSCreationDate >= ’$time.iso(\”" & bStr & “\”)’”) searchArgs:{}
–> returns POSIX path list

–NSDate -> ISO8601 Date & Time String
on retISO8601DateTimeString(targDate)
  set theNSDateFormatter to NSDateFormatter’s alloc()’s init()
  
theNSDateFormatter’s setDateFormat:“yyyy-MM-dd’T’HH:mm:ss’Z’”
  
theNSDateFormatter’s setTimeZone:(NSTimeZone’s alloc()’s initWithName:“UTC”)
  
return (theNSDateFormatter’s stringFromDate:targDate) as text
end retISO8601DateTimeString

–現在のカレンダーで指定年月のdate objectを返す(年、月、日、時、分、秒)
on getDateInternationalYMDhms(aYear, aMonth, aDay, anHour, aMinute, aSecond)
  set theNSCalendar to NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:anHour minute:aMinute |second|:aSecond nanosecond:0
  
return theDate as date
end getDateInternationalYMDhms

★Click Here to Open This Script 

2017/09/21 Finder上で指定文字列でSpotlight検索を行なった結果を表示する

FinderのWindowで指定文字列によるSpotlight検索結果を表示するAppleScriptです。

このScriptによるSpotlight検索結果は、あくまでSpotlight結果表示を行うだけのものです。本当にSpotlightによる検索結果を取得したい場合には、大量にサンプルがあるのでそちらを参考にしてください。

たったの1行ですが、Pure AppleScriptだけでは実現できない機能です。何かの実行結果により作成した文字列でSpotlight検索を行い、結果をFinder上で表示するような用途で使えそうです。

spotlight2.png
▲Finderの検索フィールドに検索文字列を入れたのと同じ状態を作ります

spotlight1.png
▲Finder上で指定文字列によるSpotlight検索結果が表示されます

AppleScript名:Finder上で指定文字列でSpotlight検索を行なった結果を表示する
– Created 2017-09-21by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4835

set aResCode to current application’s NSWorkspace’s sharedWorkspace()’s showSearchResultsForQueryString:“Framework”

★Click Here to Open This Script 

2017/09/21 迷路をRTFで作成して脱出経路を赤く着色する v2

2D迷路の作成プログラム「MazeFinder」(By Toby Jennings)をフレームワーク化したものを呼び出して、文字ベースの迷路データをRTFに変換して脱出経路情報を着色してデスクトップに書き出すAppleScriptのアップデート版です。

maze1.png

処理結果はとくに前バージョンと変化ありませんし、実行時間については前バージョンよりも0.02秒ほど余計にかかるようになりました。

では、なぜ書き換えたかといえば、各機能の再利用性の向上が目的です。せっかく書いておいても、他の用途に転用するのに手間がかかるようであれば意味がありません。さまざまな用途に転用しやすい構造(サブルーチンとしてまとめているとか)に書いておくことはとても重要です。

再利用したい機能モジュールをよりサブルーチン化するように書き換えました。こうしておくことで、まったく違った用途にもすぐに転用できます。

本Scriptを試す場合には、MazeFinder.frameworkをダウンロードして~/Library/Frameworksフォルダに入れてください。

–> Download Framework Binary

60×60の迷路を作成して着色してファイル書き出しを終了するまでに、開発環境で0.1秒程度かかりました(前バージョンは0.08秒ぐらい)。

AppleScript名:迷路をRTFで作成して脱出経路を赤く着色する v2
– Created 2017-09-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “MazeFinder” –https://github.com/tcjennings/MazeFinder
–http://piyocast.com/as/archives/4834

property NSColor : a reference to current application’s NSColor
property Board2D : a reference to current application’s Board2D
property NSUUID : a reference to current application’s NSUUID
property NSDictionary : a reference to current application’s NSDictionary
property NSMutableArray : a reference to current application’s NSMutableArray
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSFont : a reference to current application’s NSFont
property NSString : a reference to current application’s NSString
property NSDocumentTypeDocumentAttribute : a reference to current application’s NSDocumentTypeDocumentAttribute
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName
property Pathfinder : a reference to current application’s Pathfinder
property NSLiteralSearch : a reference to current application’s NSLiteralSearch
–NSNotFoundはプロパティに代入しても認識されなかった

set targFontName to “Courier-Bold” –PostScript Name
set targFontSize to 13 –Point

–迷路テキストデータ作成→Attributed Stringに
set aStr to create2DMazeAndSolveIt(30, 30, 1, 1, 28, 28) of me –Plain Text
set anAttr to changeAttrStrsFontAttribute(aStr, targFontName, targFontSize) of me –Attributed String

–迷路データに着色(参照呼び出し, Call by reference)
markCharOfAttributedString(anAttr, aStr, “!”, (NSColor’s redColor())) of me

–結果のRTFをデスクトップ上に書き出す。ファイル名はUUID.rtf
set targFol to current application’s NSHomeDirectory()’s stringByAppendingPathComponent:“Desktop”
set aRes to my saveStyledTextAsRTF(targFol, anAttr)

–指定のAttributed String内で指定文字列が含まれる箇所に指定の色をつける(結果はメイン側に参照渡し)
on markCharOfAttributedString(anAttr, origStr, aTargStr, aColor)
  set rList to searchWordWithRange(origStr, aTargStr) of me
  
repeat with ii in rList
    (anAttr’s addAttribute:(NSForegroundColorAttributeName) value:aColor range:ii)
  end repeat
end markCharOfAttributedString

–指定の文字列をAttributed Stringに変換して任意のフォントを一括指定
on changeAttrStrsFontAttribute(aStr, aFontPSName, aFontSize)
  set tmpAttr to NSMutableAttributedString’s alloc()’s initWithString:aStr
  
set aRange to current application’s NSMakeRange(0, tmpAttr’s |length|())
  
set aVal1 to NSFont’s fontWithName:aFontPSName |size|:aFontSize
  
tmpAttr’s beginEditing()
  
tmpAttr’s addAttribute:(NSFontAttributeName) value:aVal1 range:aRange
  
tmpAttr’s endEditing()
  
return tmpAttr
end changeAttrStrsFontAttribute

–指定テキストデータ(atargText)内に、指定文字列(aSearchStr)が含まれる範囲情報(NSRange)をすべて取得する
on searchWordWithRange(aTargText, aSearchStr)
  set aStr to NSString’s stringWithString:aTargText
  
set bStr to NSString’s stringWithString:aSearchStr
  
set hitArray to NSMutableArray’s alloc()’s init()
  
set cNum to (aStr’s |length|()) as integer
  
  
set aRange to current application’s NSMakeRange(0, cNum)
  
  
set aCount to 1
  
repeat
    set detectedRange to aStr’s rangeOfString:bStr options:NSLiteralSearch range:aRange
    
if detectedRange’s location is equal to (current application’s NSNotFound) then exit repeat
    
    
hitArray’s addObject:detectedRange
    
    
set aNum to (detectedRange’s location) as integer
    
set bNum to (detectedRange’s |length|) as integer
    
    
set aRange to current application’s NSMakeRange(aNum + bNum, cNum - (aNum + bNum))
    
set aCount to aCount + 1
  end repeat
  
  
return hitArray
end searchWordWithRange

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsRTF(targFol, aStyledString)
  set bstyledLength to aStyledString’s |string|()’s |length|()
  
set bDict to NSDictionary’s dictionaryWithObject:“NSRTFTextDocumentType” forKey:(NSDocumentTypeDocumentAttribute)
  
set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict
  
  
set theName to (NSUUID’s UUID()’s UUIDString())
  
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

–2D迷路を作成して脱出経路を検索して文字列で迷路データを出力する
–迷路サイズ x,y スタート座標 x, y ゴール座標 x,y
on create2DMazeAndSolveIt(xMax, yMax, xStart, yStart, xGoal, yGoal)
  set myBoard to Board2D’s alloc()’s init()
  
myBoard’s setupBoardWithRows:xMax WithColumns:yMax –60×60ぐらいが上限。正方形でないとダメ
  
myBoard’s createMazeFromBoard()
  
  
set myPathfinder to Pathfinder’s alloc()’s init()
  
set myPath to myPathfinder’s findPathThroughMaze:myBoard fromX:xStart fromY:yStart toX:xGoal toY:yGoal
  
  
set aCount to myPath’s |count|()
  
if aCount > 0 then
    set aRes to myBoard’s drawPathThroughMaze:myPath
    
return aRes as string
  else
    return false
  end if
end create2DMazeAndSolveIt

★Click Here to Open This Script 

2017/09/20 迷路をRTFで作成して脱出経路を赤く着色する

2D迷路の作成プログラム「MazeFinder」(By Toby Jennings)をフレームワーク化したものを呼び出して、文字ベースの迷路データをRTFに変換して脱出経路情報を着色してデスクトップに書き出すAppleScriptです。

maze1.png

本Scriptを試す場合には、MazeFinder.frameworkをダウンロードして~/Library/Frameworksフォルダに入れてください。

60×60の迷路を作成して着色してファイル書き出しを終了するまでに、開発環境で0.08秒程度かかりました。

–> Download Framework Binary

迷路作成の処理自体についてはとくに意味はありませんが、迷路プログラム自体にもいろいろあるようです。このMazeFinderについてはいろいろ制約条件がきつく(最大サイズ60×60程度、迷路が正方形でないとクラッシュ)、あまり迷路らしい迷路にもなっていません。

「テキストデータをRTFで出力して特定文字に着色する」というあたりの処理がミソでしょうか。

AppleScript名:迷路をRTFに作成して脱出経路を赤く着色する
– Created 2017-09-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “MazeFinder” –https://github.com/tcjennings/MazeFinder
–http://piyocast.com/as/archives/4831

property NSColor : a reference to current application’s NSColor
property Board2D : a reference to current application’s Board2D
property NSUUID : a reference to current application’s NSUUID
property NSDictionary : a reference to current application’s NSDictionary
property NSMutableArray : a reference to current application’s NSMutableArray
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSFont : a reference to current application’s NSFont
property NSString : a reference to current application’s NSString
property NSDocumentTypeDocumentAttribute : a reference to current application’s NSDocumentTypeDocumentAttribute
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName
property Pathfinder : a reference to current application’s Pathfinder
property NSLiteralSearch : a reference to current application’s NSLiteralSearch

set targFontName to “Courier-Bold” –”Courier New”/”Osaka-Mono”

–Create Maze Text
set aStr to create2DMazeAndSolveIt(30, 30, 1, 1, 28, 28) of me
set anAttr to NSMutableAttributedString’s alloc()’s initWithString:aStr

set bList to {“!”} –Mark Target List

–Set Fixed Width Font
set aRange to current application’s NSMakeRange(0, anAttr’s |length|())
set aVal1 to NSFont’s fontWithName:targFontName |size|:13
anAttr’s beginEditing()
anAttr’s addAttribute:(current application’s NSFontAttributeName) value:aVal1 range:aRange
anAttr’s endEditing()

–Change Attribute (red color)
repeat with i in bList
  set rList to searchWordWithRange(aStr, i as string) of me
  
repeat with ii in rList
    (anAttr’s addAttribute:(current application’s NSForegroundColorAttributeName) value:(NSColor’s redColor()) range:ii)
  end repeat
end repeat

–結果のRTFをデスクトップ上に書き出す
set targFol to current application’s NSHomeDirectory()’s stringByAppendingPathComponent:“Desktop”
set aFileName to (NSUUID’s UUID()’s UUIDString() as text)
set aRes to my saveStyledTextAsRTF(aFileName, targFol, anAttr)

on searchWordWithRange(aTargText, aSearchStr)
  set aStr to NSString’s stringWithString:aTargText
  
set bStr to NSString’s stringWithString:aSearchStr
  
set hitArray to NSMutableArray’s alloc()’s init()
  
set cNum to (aStr’s |length|()) as integer
  
  
set aRange to current application’s NSMakeRange(0, cNum)
  
  
set aCount to 1
  
repeat
    set detectedRange to aStr’s rangeOfString:bStr options:NSLiteralSearch range:aRange
    
if detectedRange’s location is equal to (current application’s NSNotFound) then exit repeat
    
    
hitArray’s addObject:detectedRange
    
    
set aNum to (detectedRange’s location) as integer
    
set bNum to (detectedRange’s |length|) as integer
    
    
set aRange to current application’s NSMakeRange(aNum + bNum, cNum - (aNum + bNum))
    
set aCount to aCount + 1
  end repeat
  
  
return hitArray
end searchWordWithRange

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsRTF(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings to RTF
  
set bstyledLength to aStyledString’s |string|()’s |length|()
  
set bDict to NSDictionary’s dictionaryWithObject:“NSRTFTextDocumentType” forKey:(NSDocumentTypeDocumentAttribute)
  
set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict
  
  
– Build Path
  
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

–2D迷路を作成して脱出経路を検索して文字列で迷路データを出力する
–迷路サイズ x,y スタート座標 x, y ゴール座標 x,y
on create2DMazeAndSolveIt(xMax, yMax, xStart, yStart, xGoal, yGoal)
  set myBoard to Board2D’s alloc()’s init()
  
myBoard’s setupBoardWithRows:xMax WithColumns:yMax –60×60ぐらいが上限。正方形でないとダメ
  
myBoard’s createMazeFromBoard()
  
  
set myPathfinder to Pathfinder’s alloc()’s init()
  
set myPath to myPathfinder’s findPathThroughMaze:myBoard fromX:xStart fromY:yStart toX:xGoal toY:yGoal
  
  
set aCount to myPath’s |count|()
  
if aCount > 0 then
    set aRes to myBoard’s drawPathThroughMaze:myPath
    
return aRes as string
  else
    return false
  end if
end create2DMazeAndSolveIt

★Click Here to Open This Script 

2017/09/19 DDFileReaderによる行単位でのテキストファイル読み込みテスト

オープンソースのテキスト行単位読み込みプログラム「DDFileReader」(By Dave DeLong)をCocoa Frameworkにした「DDFileReader.framework」を呼び出してテキストの行単位読み込みを行うAppleScriptです。

AppleScriptの標準命令readコマンドでも行単位読み込みができるので(掲載サンプル参照)、本フレームワークを使う必要はほとんどないのですが、とりあえず実験してみました。

本Scriptを試す場合には、DDFileReader.frameworkをダウンロードして~/Library/Frameworksフォルダに入れてください。オリジナルのままだとARC環境下でビルドできなかったので、releaseとかretainとかdeallocとかそのあたりのキーワードが含まれる行をコメントアウトして試していますが、その程度なので問題があったらぜひ知らせてください。自分でもコレを重要なプログラムで使うつもりはないのですが、問題があったら困ります。

–> Download Framework Binary

AppleScript名:DDFileReaderによる行単位でのテキストファイル読み込みテスト
– Created 2017-09-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “DDFileReader” –https://stackoverflow.com/questions/1044334/objective-c-reading-a-file-line-by-line
–http://piyocast.com/as/archives/4829

property DDFileReader : a reference to current application’s DDFileReader
property NSMutableArray : a reference to current application’s NSMutableArray

set tmpPath to POSIX path of (choose file)
set aReader to DDFileReader’s alloc()’s initWithFilePath:tmpPath

set tArray to NSMutableArray’s new()
repeat
  set aLine to aReader’s readLine()
  
if aLine = missing value then exit repeat
  
tArray’s addObject:aLine
end repeat

return length of (tArray as list)

★Click Here to Open This Script 

AppleScript名:Pure ASによるファイルの行単位読み込み
set aFile to choose file of type {“public.plain-text”}

set newList to {}

tell current application
  set fH to open for access aFile without write permission
  
try
    repeat
      set fRes to read fH as «class utf8» until return
      
log fRes
      
set the end of newList to fRes
    end repeat
  on error
    close access fH
  end try
  
end tell

return (length of newList)

★Click Here to Open This Script 

2017/09/19 Notesで選択中のnoteやfolderの情報を取得する?

defaultsコマンド経由でNotes.app(日本語ローカライズ名:メモ.app)で選択中のnoteやfolderの情報を取得する(参考になるかもしれない)AppleScriptです。

selectionとかselected folderなどの用語が用意されていないことでおなじみのNotes.app。「現在表示中のノート」を処理したいのはやまやまですが、用語辞書に書かれていない機能は、まっとうな手段では呼び出せません。

となると、「まっとうでない方法」を検討することになります。

まっとうでない手段を選ぶことで、スピード低下、安定性のなさ、メンテナンスの手間など負の側面と向き合う必要が出てきますが、問題自体の解決は行えます。

(1)GUI Scripting
まっとうでない方法の筆頭。画面上から無理やり情報を取得して、選択状態にあるGUI部品のタイトルを取得することで、選択中のノートの情報を特定できそうです。興味と必要性がないので深追いしていませんが、Notes.appの各種表示状態(アカウント情報一覧を表示するとか、表示を隠すとか)に合わせて数パターンの処理を用意しておくことで、実現できるメドは立っています。

ただし、GUI Scriptingのつねとして、「スピードは通常のAppleScriptの100倍以上遅い」(Cocoa経由の処理と比較すると数万倍遅い)「予期しないアプリケーション側の表示には追従できない」「OSやアプリケーションのバージョンアップにともない、表示内容やGUI部品の階層が変わったぐらいで書き換えが必要になる」ということから、多用は避けたいところです。

(2)defaultsコマンド
まっとうでない方法として有名。アプリケーションの設定情報を直接読んで処理します。アプリケーションやOSがバージョンアップして内部情報が変更されると動かなくなるのはGUI Scripting同様。

defaultsコマンドから得られる情報をAppleScriptネイティブのデータ形式(recordとか)に変換するのが面倒でしたが、Cocoaの機能を利用して割と手軽に読み込めるようになってきました。

Terminal上で、

  defaults read com.apple.Notes

を実行してみたところ、設定情報の中に現在選択中のnoteおよびfolderを記録している箇所があったので、実際にAppleScript中に記述して取り出してみました。

今回掲載したのが、そのdefaultsコマンドを利用した情報取得のサンプルです。このデータをもとにSQLiteのデータベースを検索してみればいいかも、などと思ってデータベースを漁ってみたものの、

notes_sqlite3_resized.png

defaultsから得られたような形式のID(UUIDっぽい?)はDB中に記録されていないようで、ちょっと残念です(SQLiteではなくCoreData経由で調べるべき?)。もう少し調べてみると手がかりが見つかるかもしれませんが、出先の電車の中で調べた程度だったのでこのぐらいです。

AppleScript名:Notesで選択中のnoteやfolderの情報をdefaults経由で取得する
– Created 2016-10-31 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4827

set sRes to do shell script ” defaults read com.apple.Notes ArchivedUIState”
set aDict to deserializeToPlistString(sRes) of me

set curNote to (aDict’s valueForKey:“currentNote”) as string
–>  ”860327F4-4E6F-44FB-92EC-720B47CDD38A”

set curNoteFol to (aDict’s valueForKey:“currentNoteFolder”) as string
–>  ”184daff620aca4ad23fd2bf70d0b76af”

set folderListHidden to (aDict’s valueForKey:“folderListHidden”) as integer as boolean
–>  false

return {curNote, curNoteFol, folderListHidden}

–XML-format plist string–> list or record
on deserializeToPlistString(aStr as string)
  set deStr to current application’s NSString’s stringWithString:aStr
  
set theData to deStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aList to current application’s NSPropertyListSerialization’s propertyListWithData:theData options:(current application’s NSPropertyListMutableContainersAndLeaves) |format|:(missing value) |error|:(missing value)
  
return aList
end deserializeToPlistString

★Click Here to Open This Script 

2017/09/15 Path Finderの最前面のウィンドウで選択中のアイテムをaliasのリストで返す

Finder代替アプリケーション「Path Finder」で最前面のウィンドウので選択中のファイル情報をaliasのlistで取得するAppleScriptです。

Cocoa Finderのパフォーマンスの低さに愛想が尽きたので、Finder代替ツールを調べていたら、Path Finder一択っぽいのでScript対応機能を調べてみたものです。

AppleScript名:Path Finderの最前面のウィンドウで選択中のアイテムをaliasのリストで返す
– Created 2017-09-15 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4826

set aList to getSelectionFromFrontmostPathFinderWindow() of me
–> {alias “Macintosh HD:Users:me:Documents:AppleScript:各種アプリケーション、Application Specific:InDesign CS3:InDesign CS3で、ドキュメント内にXML要素を作成して、移動.scpt”, alias “Macintosh HD:Users:me:Documents:AppleScript:各種アプリケーション、Application Specific:InDesign CS3:InDesign CS3で、ドキュメント内にXML要素を作成して、複製.scpt”, alias “Macintosh HD:Users:me:Documents:AppleScript:各種アプリケーション、Application Specific:InDesign CS3:InDesign CS3で、ドキュメント内のXML要素を削除.scpt”, alias “Macintosh HD:Users:me:Documents:AppleScript:各種アプリケーション、Application Specific:InDesign CS3:InDesign CS3で、指定ScriptLabelの中のテキストの、指定行の指定文字以降を行末まで取得.scpt”}

on getSelectionFromFrontmostPathFinderWindow()
  tell application “Path Finder”
    set wCount to count every finder window
    
if wCount = 0 then return false
    
    
set aSel to selection
    
if aSel = missing value then return false
    
    
set newList to {}
    
repeat with i in aSel
      set j to (path of i) as alias
      
set the end of newList to j
    end repeat
  end tell
  
return newList
end getSelectionFromFrontmostPathFinderWindow

★Click Here to Open This Script 

2017/09/15 Path Finderの最前面のウィンドウの情報を取得する

Finder代替アプリケーション「Path Finder」で最前面のウィンドウの情報を取得するAppleScriptです。

Cocoa Finderのパフォーマンスの低さに愛想が尽きたので、Finder代替ツールを調べていたら、Path Finder一択っぽいのでScript対応機能を調べてみました。

WindowをWindowと指定してはダメで、Finder Windowというオブジェクトでアクセスできることを知りました(初見なので)。

Finderと同様にWindow(正確に表現するとFinder Window)のtargetを取得できるのですが、このtargetがPath Finder独自のオブジェクト階層で表現されているのでそのままでは他のアプリケーションでは(パス情報を)使えません。

Finderオブジェクト Path Finderオブジェクト
file fsFile
folder fsFolder
disk disk

そこで、Finder Windowのtargetからpath(文字列)を取り出してaliasにcastしています。

Path Finder自体にはあまり美的センスを感じていないし、思ったよりも機能拡張を行うための機構がないため、「これではない」感じがしています。

AppleScript名:Path Finderの最前面のウィンドウの情報を取得する
– Created 2017-09-15 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4825

tell application “Path Finder”
  set wCount to count every finder window
  
if wCount = 0 then return false
  
  
tell finder window 1
    properties
    
–> {zoomable:true, closeable:true, zoomed:false, class:finder window, index:2, visible:true, name:”InDesign CS3″, current view:”list view”, miniaturizable:true, target:fsFolder “InDesign CS3″ of fsFolder “各種アプリケーション、Application Specific” of fsFolder “AppleScript” of fsFolder “Documents” of fsFolder “me” of fsFolder “Users” of disk “Macintosh HD” of application “Path Finder”, id:89809, miniaturized:false, resizable:true, bounds:{36, 27, 1029, 1111}, document:missing value}
    
    
set aTarget to (path of target) as alias
    
–> alias “Macintosh HD:Users:me:Documents:AppleScript:各種アプリケーション、Application Specific:InDesign CS3:”
  end tell
end tell

★Click Here to Open This Script 

2017/09/15 64ビット化して処理能力が低下したCocoa Finderの代替ツールはSystem Events?

macOS 10.6〜10.7で64ビット化にともないCocoaで書き直されたFinderのパフォーマンス低下が(当時から)指摘されていましたが、日常的な用途ではあまり気にしていませんでした。

Cocoa Finderのはじめのうちは、OS起動直後に表示されるFinderのウィンドウ内のアイコン描画が間に合わないのか仮アイコンで表示され、徐々に正しいアイコンで表示されるような光景もよく目にしていました(さすがに最近はそういう光景は目にしなくなりましたけれども)。

そんな中、macOS 10.13beta+MacBook Air 2011で大量のファイルを処理したときに、Finder経由で処理するとトンでもなくスピードが(SSDなのに)遅いことに気づきました。SSDに合わせて再設計したAPFS+SSDの組み合わせなのに、とてつもなく遅い。速い速いというふれ込みだったのに、Finder経由だと驚くほど遅い。

最初は、AppleScriptとFinderの間のやりとりが遅いものだと思っていましたが、実はFinderの処理そのものが遅いことが判明。数百個のファイルを選択してFinder上で(メニューから「複製」コマンドを実行して)ファイルコピーさせたりすると、めまいがするほど低速です。

そして、Appleの「AppleScript Language Guide」で「list folder」コマンド(廃止予定)を調べていたら、同コマンドの代替としてSystem Events経由でファイル情報にアクセスするやり方が掲載されているのを見つけ、Finder経由のファイル処理がすでに推奨されていない状態なのではないか、と疑いを持つようになりました。

そこで、同じファイル処理を(macOS 10.13beta上で)System EventsとFinderに対して実行してみたところ、4,224ファイル存在しているフォルダから、全ファイル名を取得する処理は、

 System Events:1秒
 Finder:6秒

同フォルダに対して、ファイル名に「99」という文字を含むファイル名だけを抽出させてみた(フィルタ参照)ところ、

 System Events:14秒
 Finder:計測不能(timeout時間を3,600秒に設定して実行してみたものの、Finderがハングアップして処理が数十分返ってこない)

といったところ。速度、信頼性ともにFinderよりもSystem Eventsの方が高いという状況です。処理対象ファイル数が100以下ぐらいであればFinder経由でも気にならないのですが、それを超えると露骨にパフォーマンス低下が顕在化します。

ただし、最近はAppleScriptでもCocoaの機能(NSFileManager)を利用してファイルの処理を行なっていたので、System Events経由のファイル処理も「驚くほど速いわけでもない」という印象。指定フォルダ内のファイルの抽出もSpotlight経由で行なっていたので、それほど不自由は感じていませんでした。

サンプルを掲載する際にも、なるべくSystem EventsかNSFileManagerを経由してファイル処理を行うようにする考えです。

Finderのコントロールは、選択中のフォルダやファイルを取得するとか、処理結果のフォルダやファイルを新規Finderウィンドウで表示させるといった用途に限定することが望ましいのでしょう(あと、指定フォルダ内のファイル一覧をas alias listで取得するのも(フィルタ参照を併用しなければ)高速です)。

2017/09/13 Safariの最前面のウィンドウの内容をすべて取得してTextEditで新規ドキュメントを作成

Safariで表示中の最前面のウィンドウのURLを取得して本文文字列を抽出し、テキストエディットの新規書類としてオープンするAppleScriptです。

たまに、Blog上の情報を部分的に利用したいようなときに、テキスト内容をコピー&ペーストで別のアプリケーションに持ってきますが、たまにテキストの選択ができないBlogに遭遇することがあります。

こんなとき、SafariのAppleScript用語辞書にはdocumentからtextが取得できるということになっていますが、目下Safari 10.1.2上のこの機能にはアクセスできません。SafariのAppleScript用語辞書はSafari 10.xになってからdocumentのpropertiesが取得できない(エラーになる)など、いまひとつです。

さらに、もっと前からdocumentのtextが取得できないようになっているので、とりあえずバグレポートで文句を書きつつ、対処してみました(Appleがバグ修正するのを待つよりも自前でScript書いた方が早いという)。

Safariの最前面のウィンドウ(frontmost document)から表示中のURLだけ取得してくれば、AppleScript単体でWeb内容のダウンロードからテキストの抽出までできるので、そのとおりに処理。取得しただけだとスクリプトエディタ上でログ表示(コピー可)するだけなので、とりあえずテキストエディットの新規書類を作成してその本文にテキストを入れてみました。

ちなみに、本Blog掲載のプログラムリストの末尾に「スクリプトエディタに内容を転送するリンク」をつけていますが、これはコピー&ペーストではなく、カスタムURLプロトコル(applescript://)経由でプロセス間通信によりWebブラウザからスクリプトエディタにデータ転送する仕組みを利用しています。一般ユーザーにとっては、「コピー&ペーストではない」という点がなかなか理解できないようです。

AppleScript名:Safariの最前面のウィンドウの内容をすべて取得してTextEditで新規ドキュメントを作成
– Created 2017-09-09 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/4823

property NSAttributedString : a reference to current application’s NSAttributedString
property NSData : a reference to current application’s NSData
property |NSURL| : a reference to current application’s |NSURL|

–Get URL from frontmost window (document)
tell application “Safari”
  if (every document) = {} then return
  
tell front document
    set aURLstr to URL
  end tell
end tell

–Get String from Safari URL
set sRes to getStringFromRemoteHTML(aURLstr) of me

–Make new document with TextEdit to browse or edit text contents
tell application “TextEdit”
  make new document with properties {text:sRes}
  
activate
end tell

on getStringFromRemoteHTML(aURLstr as string)
  set theURL to |NSURL|’s URLWithString:aURLstr
  
set theData to NSData’s dataWithContentsOfURL:theURL
  
set attStr to NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)
  
if attStr = missing value then error “Internet Connection lost or Wrong URL”
  
return (attStr’s |string|()) as string
end getStringFromRemoteHTML

★Click Here to Open This Script 

2017/09/13 macOS 10.13, High Sierra 9/26リリース、iTunes 12.7

macOS 10.13, High Sierraが日本時間で2017/9/26にリリースされることが発表されました。巨大なバグはバグではなく仕様として開き直って、リリース後のアップデートでなんとかしようという話のようです(日本語音声読み上げバグは直らないことが決定)。

リリース日が予想よりも1か月早いですが、これには9〜10月に新ハードウェア(iMac Proとか)をリリースするための措置でしょう。あるいは、iOSデバイスをTouch bar化するApple純正アプリケーション(サードパーティ製は1年前から存在していた)をiOS11用に提供し、macOSのアップデートとタイミングを揃える必要があったとか(願望をこめた予想)。

一方で、本日iTunes 12.7がリリースされました。High Sierraよりもこちらの変更のほうが変動幅が大きいかもしれません。

 ・着信音(ringtone)をMac上のiTunes上で扱わなくなった
 ・ポップアップメニューから「iTunes U」が削除された
 ・iOSデバイスのモバイルアプリストアが削除された

iTunes 12.7でAppleScript用語辞書の変更はありません(用語辞書だけで比較)。画面上で削除された機能について、iTunes 12.7のAppleScript用語内には、

media kind alert tone / audiobook / book / home video / iTunesU / movie / song / music video / podcast / ringtone / TV show / voice memo / unknown — the media kind of the track

と、定義が残ったままです(このあたりいつものApple仕事。善意に解釈できないこともないものの、善意に解釈するとバカを見るところ)。

iPhoneの発表会で噂どおりの顔認識機能(Face ID)を搭載したiPhone Xが登場しました。予想どおり赤外線カメラを搭載し(可視光線からの画像だけでは顔認識の精度を上げられないので)ドットプロジェクタを併用しているため、OSレベルでのソフトウェアによる顔認識能力が上がったわけではなさそうです。

2017/09/10 HTMLをplain textに変換 v2

HTMLのファイルをplain textに変換するAppleScriptです。

前バージョンでは、,△蕕じめHTMLを変数に読み込んでおいて、△修離如璽燭NSString経由でNSAttributedStringに(文字エンコーディングを指定しつつ)読み込んでいました。

このため、,涼奮で文字コードエンコーディングの判定がうまく行かないと、正しいPlain Textが得られないという「弱点」がありました(一応、対策のためにテキストエディタ並みの日本語テキストエンコーディング自動判定ライブラリを併用していますが、毎回掲載するわけにもいきません)。

これに対してShane Stanleyから「こうしたほうが」というツッコミがあったのが本Scriptです。HTMLファイルのファイルパスをNSDataに渡して読み込む、▲如璽燭NSAttaributedStringに読み込む という処理に変更。これまで読み込みに問題のあった(テキストエンコーディング判定失敗)HTMLも正しく読み込めているようです。

AppleScript名:HTMLをplain textに変換 v2
– Created 2017-09-09 by Takaaki Naganoya
– Created 2017-09-09 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4821

property NSAttributedString : a reference to current application’s NSAttributedString
property NSData : a reference to current application’s NSData

set aFile to choose file
set aPlainText to HTMLDecode(POSIX path of aFile) of me

on HTMLDecode(thePath)
  set theData to NSData’s dataWithContentsOfFile:thePath
  
set attStr to NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)
  
return (attStr’s |string|()) as string
end HTMLDecode

★Click Here to Open This Script 

2017/09/09 macOS 10.13betaのFinderのScriptingが遅い

macOS 10.13 High Sierraの検証をMacBook Air 2011(Core i5 RAM 4GB)で行なっていますが、この環境が非力なためなのか、Finderに対してファイル操作を行うとハングアップしたり遅くなったりするなどの問題が見られています。

このままリリースしたら問題になるレベルです。NSFileManager経由でファイル操作を行うと問題がないため、

 (1)Finderがメモリを余計に喰うようになっている
 (2)FinderのAppleScript向けの機能に問題がある
 (3)Beta版のソフトウェアのため、処理に冗長な部分がある(ログ記録など)

などの事態も考えられますが、現状では相当まずい状態です。おそらくRAM 8GBの環境であれば問題は少ないことも期待できますが、自分がテストに使っている構成だとFinderへのScriptingが使い物になりません。

→ 特定条件下(1フォルダ内のファイル数が多い。4,000ファイルで実験)でFinder自体の応答性が極端に低下するようにも見えます。つまり、FinderとASの間が問題なのではなく、Finder自体にパフォーマンス上の問題が?

Shane Stanleyにも実際のScriptをやりとりして検証してもらいましたが、やはり同様にフィルタ参照でファイルの抽出などを行うと極端にパフォーマンスが落ちることが確認されました。

2017/09/08 HTMLをplain textに変換(UTF8)

HTMLのファイルをplain textに変換するAppleScriptです。

HTMLのplain text化とくに日本語テキスト入りのHTMLのPlain Text化は昔(Mac OS X 10.4ぐらいの時代)はひじょうに厄介な処理でした。それが、Cocoaの機能を使うと割と簡単にできるようになり、AppleScriptによる処理の「死角」が少なくなってきた印象があります。

HTMLのテキストエンコーディングについては、ヘッダーに書かれている文字コードと実際の文字コードが異なる可能性もあるため(ありがち)、いまのところテキストのエンコーディング自動判別ルーチンを併用しています。ただ、もうちょっと簡潔に処理できないかとも思うところです(2パスでHTMLを読み込んで、1パス目ではヘッダー部分のみ読み取ってエンコーディング情報を取得、2パス目で取得したエンコーディング情報に基づいて全体を読み直し、とか?)。

本ルーチンは、掲載のためにとりあえずHTMLがUTF-8で書かれているという前提にもとづいて処理を行なっています。

→ 改修版(v2)はこちら

AppleScript名:HTMLをplain textに変換(UTF8)
– Created 2017-09-08 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/4818

property NSUnicodeStringEncoding : a reference to current application’s NSUnicodeStringEncoding
property NSAttributedString : a reference to current application’s NSAttributedString
property NSString : a reference to current application’s NSString

set aFile to choose file
set aRes to read aFile as «class utf8» –文字エンコーディング自動判定処理を行なったほうがよい
set aPlainText to HTMLDecode(aRes) of me

on HTMLDecode(HTMLString)
  set theString to NSString’s stringWithString:HTMLString
  
set theData to theString’s dataUsingEncoding:(NSUnicodeStringEncoding)
  
set attStr to NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)
  
return (attStr’s |string|()) as string
end HTMLDecode

★Click Here to Open This Script 

2017/09/08 iTunes Library上のsongでライブラリへの追加年を集計してKeynote書類上にグラフ作成

iTunesのMusic Libraryで楽曲のライブラリへの追加年で集計を行ってKeynote上にグラフを作成するAppleScriptです。6,775曲が登録されている自分のライブラリで集計→グラフ作成で3秒程度です。

itunes_usic.png

iTunes LibraryへのアクセスをiTunesLibrary framework経由で行い、NSCountedSetで集計を行うことで高速に処理を行ないます。

iTunesの基礎的な操作については電子書籍「iTunes Control」にて、Keynoteのグラフ作成については、電子書籍「Keynote Control with AppleScript」 △脳楮戮望匆陲靴討い泙后6縮がある方はぜひお買い求めください。

年々、楽曲を聴かない&買わなくなっている様子が見てとれますが、ほかの人はどんなもんなのか興味があります。

AppleScript名:iTunes Library上のsongでライブラリへの追加年で集計してKeynote書類上にグラフ作成
– Created 2017-09-06 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “iTunesLibrary”
–http://piyocast.com/as/archives/4816

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

set yRec to retSongAddedYear() of me
–>  {{theName:2005, numberOfTimes:1907}….}

–楽曲のiTunesライブラリへの追加「年」のみ抽出
set yList to ((NSArray’s arrayWithArray:yRec)’s valueForKeyPath:“theName”) as list
–>  {2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017}

–楽曲のiTunesライブラリへの追加「年」ごとのカウントを抽出
set vList to ((NSArray’s arrayWithArray:yRec)’s valueForKeyPath:“numberOfTimes”) as list
–>  {1907, 1125, 853, 319, 638, 353, 351, 241, 605, 344, 71, 76, 28}

tell application “Keynote”
  set adoc to (make new document with properties {document theme:theme “ホワイト”, height:768, width:1024}) –Caution: theme name is **localized** (”White”)
  
tell front document
    set base slide of current slide to master slide “空白” –Caution: master slide name is **localized** (”Blank”)
    
tell current slide
      add chart row names {“ライブラリ追加年”} column names yList data {vList} type vertical_bar_2d group by chart row –row name is “iTunes Library Added Year”
    end tell
  end tell
  
activate
end tell

on retSongAddedYear()
  set library to ITLibrary’s libraryWithAPIVersion:“1.0″ |error|:(missing value)
  
if library is equal to missing value then return {}
  
  
set allTracks to library’s allMediaItems()
  
set allCount to allTracks’s |count|()
  
  
set anEnu to allTracks’s objectEnumerator()
  
set newArray to NSMutableArray’s alloc()’s init()
  
  
repeat
    set aPL to anEnu’s nextObject()
    
if aPL = missing value then exit repeat
    
try
      set aKind to (aPL’s mediaKind) as integer
      
if (aKind as integer) is equal to 2 then –Music, Song
        set pMonth to ((aPL’s addedDate() as date)’s year) as integer
        
newArray’s addObject:(pMonth)
      end if
    on error
      set aLoc to (aPL’s location’s |path|()) as string
    end try
  end repeat
  
  
return countItemsByItsAppearance(newArray) of me
end retSongAddedYear

–出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to NSSortDescriptor’s sortDescriptorWithKey:“theName” ascending:true
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

★Click Here to Open This Script 

2017/09/07 小説家になろうサイトでダウンロード対象作品テキストを全話ダウンロード

「小説家になろう」サイトに掲載中の小説の「テキストをダウンロード」ウィンドウを表示させた状態で実行すると、全話ダウンロードするAppleScriptです。「Knight’s & Magic」を全話ダウンロードするために作ったものです。

典型的な「作り捨て」Scriptですが、別に「Knight’s & Magic」向けにカスタマイズしているわけではないので、同サイト掲載中のすべての作品で使えるはずです。

最初に1回ポップアップボタンをクリックしてメニューを表示させ、話数をそこから取得します。

ダウンロードボタンの状態を監視して、前の作品のダウンロードが終わってボタンがクリックできるようになるまで待機します。ダウンロードする側のScriptと、ダウンロード先のフォルダに仕掛けたフォルダアクションScriptの間でハンドシェイクを行えば、ダウンロード完了するたびにファイル名を(掲載タイトルのとおりに)リネームするといった処理も実現できますが、本Scriptにはそこまで仕込んでいません。

Safariをコントロールしているため、Safariの「開発」メニューを表示させて、同メニューの「スマート検索フィールドからのJavaScriptを許可」「AppleEventからのJavaScriptを許可」の両方をチェックしておく必要があります。また、GUI Scriptingを利用しているため、「システム環境設定」の「セキュリティとプライバシー」>「プライバシー」>「アクセシビリティ」でScript Editorにコンピュータの制御を許可してある必要があります(書籍「AppleScript最新リファレンス」を参照してください)。

OSやSafariのバージョンが変わると動かなくなるかもしれませんが、そのあたりはGUI Scriptingの宿命ということで。また、GUI部品の情報をまるごと取得して変更を吸収する機構も組みかけていたのですが、さすがに掲載するには高度すぎたので、この初期バージョンを掲載しています。

nightuma1.png

nightuma2.png

nightuma3.png
▲この状態でAppleScriptを実行すると、ポップアップメニュー内容を変更して「ダウンロード実行します」ボタンをクリック(あとは、話数分だけ自動認識して繰り返し)

AppleScript名:小説家になろうサイトでダウンロード対象作品テキストを全話ダウンロード
– Created 2017-09-01 by Takaaki Naganoya
– 2017 Piyomaru Software
–OS Ver: macOS 10.12.6
–Safari Ver: 10.1.2

–http://piyocast.com/as/archives/4815

–ダウンロード話数をカウントする
set mCount to getMenuItems() of me

–1話から順次ダウンロードを行う
repeat with i from 1 to mCount
  –ダウンロード対象話を選択
  
selectMenuItemAtIndex(i) of me
  
delay 1
  
  
–ダウンロードボタンをクリック(イネーブルになるまで待つ)
  
clickDownloadButton() of me
  
delay 1
end repeat

–「ダウンロード実行します」ボタンをクリック
on clickDownloadButton()
  –activate application “Safari”
  
tell application “System Events”
    tell process “Safari”
      tell button 1 of UI element 1 of row 3 of table 1 of UI element 1 of scroll area 1 of group 1 of group 1 of tab group 1 of splitter group 1 of window 1
        repeat 1000 times
          set anEnabled to enabled
          
if anEnabled = true then
            click
            
return
          end if
          
delay 0.5
        end repeat
      end tell
    end tell
  end tell
end clickDownloadButton

–指定番号のメニューアイテムをクリック(選択)する
on selectMenuItemAtIndex(indNum)
  –activate application “Safari”
  
tell application “System Events”
    tell process “Safari”
      –Click “ダウンロードする部分番号” menu
      
tell pop up button 1 of group 1 of UI element 1 of row 2 of table 1 of UI element 1 of scroll area 1 of group 1 of group 1 of tab group 1 of splitter group 1 of window 1
        click
      end tell
      
      
tell menu 1 of group 1 of tab group 1 of splitter group 1 of window 1
        set mCount to count every menu item –1-based index count
        
        
tell menu item indNum
          click –Select popup menu item
        end tell
        
      end tell
      
return mCount
    end tell
  end tell
end selectMenuItemAtIndex

–指定のポップアップメニューのアイテム数をカウント
on getMenuItems()
  –activate application “Safari”
  
tell application “System Events”
    tell process “Safari”
      –Click “ダウンロードする部分番号” menu
      
tell pop up button 1 of group 1 of UI element 1 of row 2 of table 1 of UI element 1 of scroll area 1 of group 1 of group 1 of tab group 1 of splitter group 1 of window 1
        click –Open Menu
      end tell
      
      
tell menu 1 of group 1 of tab group 1 of splitter group 1 of window 1
        set mCount to count every menu item –Count menu items
        
tell menu item 1
          click –Close Menu
        end tell
      end tell
      
return mCount
    end tell
  end tell
end getMenuItems

★Click Here to Open This Script 

2017/09/06 文字種類変換(ASOC)

macOS 10.12以降で利用できるNSStringの文字種類変換機能を呼び出して手軽に文字種類変換を行うAppleScriptです。

Shane Stanleyから「macOS 10.12以降なら全角半角文字変換が簡単にできるよー」と、メールで教えてもらいました。

実際にAppleのWeb Referenceで確認してみたら、半角⇄全角変換以外にもいろいろ変換機能が用意されているのを見つけたので、まとめておきました。

ここに挙げた以外にもいろいろ文字種類変換メソッドはあるのですが、日本語に関係するもののみピックアップしました。

AppleScript名:文字種類変換(ASOC)
– Created 2017-09-06 by Shane Stanley
– Modified 2017-09-06 by Takaaki Naganoya
use AppleScript version “2.5″ – (10.12) or later
use framework “Foundation”
use scripting additions
–http://piyocast.com/as/archives/4811

property NSString : a reference to current application’s NSString
property NSStringTransformFullwidthToHalfwidth : a reference to current application’s NSStringTransformFullwidthToHalfwidth
property NSStringTransformHiraganaToKatakana : a reference to current application’s NSStringTransformHiraganaToKatakana
property NSStringTransformLatinToHiragana : a reference to current application’s NSStringTransformLatinToHiragana
property NSStringTransformLatinToKatakana : a reference to current application’s NSStringTransformLatinToKatakana
property NSStringTransformToUnicodeName : a reference to current application’s NSStringTransformToUnicodeName
property NSStringTransformToXMLHex : a reference to current application’s NSStringTransformToXMLHex

set a01 to hanToZen(“トウキョウト”) of me
–>  ”トウキョウト”–Zenkaku (Full Width)

set a02 to zenToHan(a01) of me
–>  ”トウキョウト” –Hankaku (Half Width)

set a03 to katakanaToHiraganaTo(a01) of me
–>  ”とうきょうと”

set a04 to hiraganaToKatakana(a03) of me
–>  ”トウキョウト”

set a05 to hiraganaToalphabet(a03) of me
–>  ”toukyouto”

set a06 to alphabetToHiragana(a05) of me
–>  ”とうきょうと”

set a07 to katakanaToAlphabet(a04) of me
–>  ”toukyouto”

set a08 to alphabetToKatakana(a07) of me
–>  ”トウキョウト”

set a09 to characterToUnicodeName(“あ”) of me
–>  ”\\N{HIRAGANA LETTER A}”

set a10 to unicodeNameToCharacter(a09) of me
–>  ”あ”

set a11 to stringToXMLHex(“あ”) of me
–>  ”あ”

set a12 to xmlHexTostring(a11) of me
–>  ”あ”

–半角→全角変換
on hanToZen(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformFullwidthToHalfwidth) |reverse|:true) as string
end hanToZen

–全角→半角変換
on zenToHan(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformFullwidthToHalfwidth) |reverse|:false) as string
end zenToHan

–ひらがな→カタカナ変換
on hiraganaToKatakana(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformHiraganaToKatakana) |reverse|:false) as string
end hiraganaToKatakana

–カタカナ→ひらがな変換
on katakanaToHiraganaTo(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformHiraganaToKatakana) |reverse|:true) as string
end katakanaToHiraganaTo

–ローマ字→ひらがな変換
on alphabetToHiragana(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToHiragana) |reverse|:false) as string
end alphabetToHiragana

–ひらがな→ローマ字変換
on hiraganaToalphabet(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToHiragana) |reverse|:true) as string
end hiraganaToalphabet

–ローマ字→カタカナ変換
on alphabetToKatakana(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToKatakana) |reverse|:false) as string
end alphabetToKatakana

–カタカナ→ローマ字変換
on katakanaToAlphabet(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToKatakana) |reverse|:true) as string
end katakanaToAlphabet

–文字→Unicode Name変換
on characterToUnicodeName(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformToUnicodeName) |reverse|:false) as string
end characterToUnicodeName

–Unicode Name→文字変換
on unicodeNameToCharacter(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformToUnicodeName) |reverse|:true) as string
end unicodeNameToCharacter

–文字→XML Hex変換
on stringToXMLHex(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformToXMLHex) |reverse|:false) as string
end stringToXMLHex

–XML Hex→文字変換
on xmlHexTostring(aStr)
  set aString to NSString’s stringWithString:aStr
  
return (aString’s stringByApplyingTransform:(NSStringTransformToXMLHex) |reverse|:true) as string
end xmlHexTostring

★Click Here to Open This Script 

2017/09/06 郵便番号から住所を求める

アイビスのWeb APIを呼び出して(日本国内の)郵便番号から住所を求めるAppleScriptです。

とくにユーザー登録などを行わずにAPIを呼び出せます。ただし、実行結果に半角カタカナを使っているなど、とても不思議な仕様なので、適宜半角→全角変換を行う必要があるものと思われます(21世紀に入って20年近くになるのに、前世紀の遺物である「半角カタカナ」を見ることになるとは、、、)。

いろいろテスト実行してみると、夜間に停止していたり(AWSで動かしているようですが)するように見えます。

郵便番号から住所を検索するサービスといえば、XML-RPC経由での問い合わせが可能な「郵便専門ネット」が存在しています。
→ 郵便専門ネットでXML-RPC経由で郵便番号から住所を返す

さすがに「平成の大合併」といわれた、ひとときの市町村統合の動きは鎮静化していますが、郵便番号はビル単位での指定が可能なので逆に言えばビルの建て替えや統合などによる影響をつねに受け続ける状態であるともいえます。こまめに最新データにキャッチアップしてくれるサービスを選んで使いたいところです。

AppleScript名:郵便番号から住所を求める
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://www.ibsnet.co.jp/solution/zipcloud.html
–http://piyocast.com/as/archives/4810

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLQueryItem : a reference to current application’s NSURLQueryItem
property NSURLComponents : a reference to current application’s NSURLComponents
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSURLConnection : a reference to current application’s NSURLConnection

set aCode to “176-0022″
set aRec to retAddressFromZipCode(aCode) of me
–>  {kana3:”コウヤマ”, address1:”東京都”, zipcode:”1760022″, kana2:”ネリマク”, prefcode:”13″, address3:”向山”, kana1:”トウキョウト”, address2:”練馬区”}
set aStr to (address1 of aRec) & (address2 of aRec) & (address3 of aRec)
–>  ”東京都練馬区向山”

on retAddressFromZipCode(aCode)
  set reqURLStr to “http://zipcloud.ibsnet.co.jp/api/search”
  
set aRec to {zipcode:aCode}
  
set bURL to retURLwithParams(reqURLStr, aRec) of me
  
set aRes to callRestGETAPI(bURL) of me
  
set aRESTres to json of aRes
  
set aRESCode to responseCode of aRes
  
set aRESHeader to responseHeader of aRes
  
return results of aRESTres as record
end retAddressFromZipCode

–GET methodのREST APIを呼ぶ(パラメータをあらかじめURLに入れておくタイプ)
on callRestGETAPI(aURL)
  –Request
  
set aRequest to NSMutableURLRequest’s requestWithURL:(|NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setTimeoutInterval:60
  
set aRes to NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
  
–Parse Results
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to NSString’s alloc()’s initWithData:bRes encoding:(NSUTF8StringEncoding)
  
  
set jsonString to NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to (dRes’s statusCode()) as integer
  
  
–Get Response Header
  
set resHeaders to (dRes’s allHeaderFields()) as record
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPI

on retURLwithParams(aBaseURL, aRec)
  set aDic to NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

★Click Here to Open This Script 

2017/09/06 ツイ4のページで新規連載マンガの画像を取得してPDF化(新規連載のPDF化)v3

Safariで表示中のWebマンガサイト「ツイ4」(更新情報をTwitterに投稿)のマンガを全エピソードダウンロードしてPDFにまとめるAppleScriptです。

実行にあたってはShane StanleyのAppleScript Libraries「BridgePlus」のインストールを必要とします(~/ibrary/Script Librariesフォルダに入れるだけ)。

実行開始時にはSafariでツイ4の特定のマンガのページをオープンしている必要があります。

tui4.png

Safariの最前面のウィンドウからURLやTitle、リンクされている画像の詳細情報を取得し、条件チェックなどを行なったのちに詳細なデータの抽出を行います。

次に、PDFの保存先を選択するダイアログを表示。このさい、デフォルトの保存先を「ピクチャ」フォルダ、ファイル名をマンガのタイトルに指定。

ページにリンクされていた画像(ツイではファイル名はシーケンシャル番号)から番号の情報だけを抽出して最大値、最小値を計算。この範囲で画像のダウンロード、PDFへの追記を行います。ただし、実運用してみたところ、Safariからすべての画像を取得できないようで(非同期表示しているようなので)、とりあえず1〜9999までの番号の画像を順次ダウンロードし、画像が存在しなければ処理を終了しています。

画像をダウンロードするたびにPDFに追記していますが、このあたりは途中でエラーが出て停止してもそれまでの処理内容が保存されることを意図してのことです。SSD搭載機では問題のない処理ですが、HDD搭載機では若干遅く感じるかもしれません(もはやHDD搭載機が身の回りにないので不明)。

これまでは、マンガの新規連載がはじまるとcurlコマンドで画像をダウンロードしてPDFに連結する作業を手で行なっていたのですが(誰も頼んでねえよ)、新規連載が増えたので自動化してみました。それでもありあわせの部品を組み合わせただけなので、それほど手間はかかっていません。

本Scriptとは別に更新された差分をPDFに連結するAppleScriptを作って日々実行し、大きな画面でブラウズするのに役立てています。割とこういう、ごくごく私的なScriptで野心的な処理を先行してテストしているものです。

AppleScript名:ツイ4のページで新規連載マンガの画像を取得してPDF化(新規連載のPDF化)v3
– Created 2016-09-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “QuartzCore”
use BridgePlus : script “BridgePlus”
–http://piyocast.com/as/archives/4808

property SMSForder : a reference to current application’s SMSForder
property |NSURL| : a reference to current application’s |NSURL|
property NSURLRequest : a reference to current application’s NSURLRequest
property NSURLConnection : a reference to current application’s NSURLConnection
property NSArray : a reference to current application’s NSArray
property NSFileManager : a reference to current application’s NSFileManager
property NSNumberFormatter : a reference to current application’s NSNumberFormatter
property NSPredicate : a reference to current application’s NSPredicate
property PDFPage : a reference to current application’s PDFPage
property PDFDocument : a reference to current application’s PDFDocument
property NSURLRequestUseProtocolCachePolicy : a reference to current application’s NSURLRequestUseProtocolCachePolicy
property NSNumberFormatterPadBeforePrefix : a reference to current application’s NSNumberFormatterPadBeforePrefix
property NSImage : a reference to current application’s NSImage
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSNumber : a reference to current application’s NSNumber
property NSRegularExpressionDotMatchesLineSeparators : a reference to current application’s NSRegularExpressionDotMatchesLineSeparators
property NSRegularExpressionAnchorsMatchLines : a reference to current application’s NSRegularExpressionAnchorsMatchLines
property NSRegularExpression : a reference to current application’s NSRegularExpression
property NSString : a reference to current application’s NSString

property theTargetSite : “http://sai-zen-sen.jp/”

tell application “Safari”
  if (count every document) = 0 then
    display notification “Safari does not open web page”
    
return
  end if
  
  
set docTitle to (do JavaScript “document.title” in front document) –Title
  
  
tell front document –URL
    set aURL to URL
  end tell
end tell

if aURL does not start with theTargetSite then
  display notification “This site is not the target”
  
return
end if

–Safariの最前面のウィンドウから画像リンクをすべて取得(Height, Width, URL)
set aList to getImageSizeAndURLOfFrontSafariDocument() of me

–取得した画像情報の2D Listをサイズで降順ソート
load framework –Force loading BridgePlus framework
set sortIndexes to {0, 1} –Key Item id: begin from 0
set sortOrders to {false, false}
set sortTypes to {“compare:”, “compare:”}
set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value))

–画像が取得できなかったら処理終了
if (resList as list) = {} then
  display notification “There is no images on this page”
  
return –No Result
end if

–最大サイズの画像情報を取得する(おそらくマンガ)
set {maxHeight, maxWidth, maxURL} to contents of first item of (resList as list)

set aNSURL to |NSURL|’s URLWithString:maxURL
set aNSURLfilename to (aNSURL’s lastPathComponent())
set aNSURLpure to aNSURL’s URLByDeletingLastPathComponent()
set aNSURLextension to aNSURLfilename’s pathExtension() as string
set aNSURLfilenameLen to (aNSURLfilename’s stringByDeletingPathExtension())’s |length|() as integer –画像ファイル名から拡張子を除去した部分の文字列長

–画像情報リストを画像サイズで抽出
set maxHeightStr to (maxHeight as integer) as string
set maxWidthStr to (maxWidth as integer) as string
set thePred to NSPredicate’s predicateWithFormat:(“(self[0] == “ & maxHeightStr & “) AND (self[1] == “ & maxWidthStr & “)”)
set bArray to (resList’s filteredArrayUsingPredicate:thePred) as list

–URLからファイル名の数値部分のみ抽出
set imageArray to current application’s NSMutableArray’s new()
repeat with i in bArray
  set j to contents of last item of i –(Image URL)
  
set aTmpURL to (|NSURL|’s URLWithString:j)
  
set aTmpfilename to (aTmpURL’s lastPathComponent()) as string
  
set numStr to first item of (my findPattern:(“^\\d{1,” & (aNSURLfilenameLen as string) & “}”) inString:aTmpfilename)
  
set jj2 to (SMSForder’s transformedFrom:numStr ICUTransform:“Fullwidth-Halfwidth” inverse:false) as integer
  (
imageArray’s addObject:jj2)
end repeat

–ファイル名から抽出した数値の最小値と最大値を求める。ただ、実運用したらWeb側から画像をすべて取得されない(非同期読み込みを行なっているらしい)ケースがあったため、ここの値は参考値程度にしか使えなかった
set maxRes to (imageArray’s valueForKeyPath:“@max.self”)’s intValue() –最大値
set minRes to (imageArray’s valueForKeyPath:“@min.self”)’s intValue() –最小値
log {minRes, maxRes}

–PDFのファイル名と場所をユーザーに確認
set pdfFile to (choose file name with prompt “Select PDF Name & Location” default location (path to pictures folder) default name (docTitle & “.pdf”))
set pdfFilePOSIX to POSIX path of pdfFile
set newFilePath to current application’s NSString’s stringWithString:pdfFilePOSIX

–Make Blank PDF
set aPDFdoc to PDFDocument’s alloc()’s init()

–Download each image and append to blank PDF
set insCount to 1 –画像ダウンロード用のページ数(Loop Counter)とPDF連結用のページ番号(insCount)を分離

–repeat with i from minRes as integer to maxRes as integer
repeat with i from 1 to 9999
  –URL部品の連結
  
set aFILENAME to numToZeroPaddingStr(i, aNSURLfilenameLen, “0″) of me
  
set aFULLURL to (aNSURLpure’s absoluteString() as string) & (aFILENAME as string) & “.” & (aNSURLextension as string)
  
set aURL to (|NSURL|’s URLWithString:aFULLURL)
  
  
–URL(画像)をダウンロード
  
set {uRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
  
  
if uRes = true then
    display notification “Episode “ & (i as string) & ” exists…”
    
set bImage to (NSImage’s alloc()’s initWithData:aData)
    (
aPDFdoc’s insertPage:(PDFPage’s alloc()’s initWithImage:bImage) atIndex:(insCount - 1))
    (
aPDFdoc’s writeToFile:newFilePath) –1Page更新するたびにファイル保存
    
set changedF to true –PDFにページが追記されたことを検出
  else
    display notification “No more new episode….”
    
exit repeat
  end if
  
  
set insCount to insCount + 1
end repeat

–FinderコメントにURLを記入
tell application “Finder”
  set comment of (pdfFile as alias) to (aNSURLpure’s absoluteString() as string)
end tell

–生成したPDFをオープン。ビューワー経由ではなくFinder経由でopen命令を送って表示
tell application “Finder”
  open (pdfFile as alias)
end tell
–ここで処理終了

—————

on getImageSizeAndURLOfFrontSafariDocument()
  set aList to {}
  
  
tell application “Safari”
    if its running then
      if (count every document) = 0 then return {}
      
set aRes to (do JavaScript “document.images.length” in front document)
      
      
repeat with i from 0 to (aRes - 1)
        set aHeight to do JavaScript ((“document.images[” & i as string) & “].height”) in front document
        
set aWidth to do JavaScript ((“document.images[” & i as string) & “].width”) in front document
        
set aSRC to do JavaScript ((“document.images[” & i as string) & “].src”) in front document
        
set the end of aList to {aHeight, aWidth, aSRC}
      end repeat
    end if
  end tell
  
  
return aList
end getImageSizeAndURLOfFrontSafariDocument

on findPattern:thePattern inString:theString
  set theOptions to ((NSRegularExpressionDotMatchesLineSeparators) as integer) + ((NSRegularExpressionAnchorsMatchLines) as integer)
  
set theRegEx to NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
  
set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString}
  
set theFinds to theFinds as list
  
set theResult to {}
  
set theNSString to NSString’s stringWithString:theString
  
  
repeat with i in theFinds
    set theRange to (contents of i)’s range()
    
set end of theResult to (theNSString’s substringWithRange:theRange) as string
  end repeat
  
return theResult
end findPattern:inString:

–1D List(文字)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DList:theList ascOrder:aBool
  set aDdesc to NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DList:ascOrder:

–整数の値に指定桁数ゼロパディングして文字列で返す
on numToZeroPaddingStr(aNum as integer, aDigit as integer, paddingChar as text)
  set aNumForm to NSNumberFormatter’s alloc()’s init()
  
aNumForm’s setPaddingPosition:(NSNumberFormatterPadBeforePrefix)
  
aNumForm’s setPaddingCharacter:paddingChar
  
aNumForm’s setMinimumIntegerDigits:aDigit
  
  
set bNum to NSNumber’s numberWithInt:aNum
  
set aStr to aNumForm’s stringFromNumber:bNum
  
  
return aStr as text
end numToZeroPaddingStr

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (NSURLRequest’s requestWithURL:aURL cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to 404
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

–指定PDFのページ数をかぞえる(10.9対応。普通にPDFpageから取得)
–返り値:PDFファイルのページ数(整数値)
on pdfPageCount(aFile)
  set aFile to POSIX path of aFile
  
set theURL to |NSURL|’s fileURLWithPath:aFile
  
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:theURL
  
set aRes to aPDFdoc’s pageCount()
  
return aRes as integer
end pdfPageCount

★Click Here to Open This Script 

2017/09/05 japaneseTokenizeのじっけん3

Objective-Cで記述した日本語形態素解析フレームワーク「japaneseTokenize」のアップデート版を呼び出し、テキストを文章単位にparseするAppleScriptです。

AppleScriptネイティブの予約語にも「paragraphs of」というものがあり、テキストを改行コード単位で分割してリスト(配列)にして返してくれます。ただ、これだと用途が限定されるので文章単位でparseするメソッドをFrameworkに追加してみました。

# 言葉の意味的に「paragraphs of」ではなく「sentences of」(そんなものはない)に該当する挙動なので、修正して後日掲載

本Scriptを試す場合には、最新版のjapaneseTokenize.framework(v1.1)をダウンロードして~/Library/Frameworksフォルダに入れてください。以前のバージョンのフレームワークがあったら削除してください。

–> Download Framework Binary

AppleScript名:japaneseTokenizeのじっけん3
– Created 2017-09-04 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “japaneseTokenize”
–https://github.com/murakami/workbook/tree/master/mac/Ruby
–http://d.hatena.ne.jp/shu223/20130318/1363566717

–http://piyocast.com/as/archives/4806

set targString to “これら2つの形態素解析機能のparseの結果が矛盾しているかどうかは未確認ですか? 「今日のばんごはんに何を作ろうか?」 短い文章では同じことを確認してありますが、長い文章でも同じかどうかは未確認。多分同じだとは思いますけれども。”

set aRes to current application’s jTokenize’s parseToParagraphs:targString
set bList to (aRes’s valueForKeyPath:“token”) as list
–> {”これら2つの形態素解析機能のparseの結果が矛盾しているかどうかは未確認ですか? “, “「今日のばんごはんに何を作ろうか?」 ”, “短い文章では同じことを確認してありますが、長い文章でも同じかどうかは未確認。”, “多分同じだとは思いますけれども。”}

★Click Here to Open This Script 

2017/09/05 指定フォルダ以下のテキストファイルのファイル名冒頭についている数字から欠番を求める

ファイル名の先頭に1〜3桁の数字がついているテキストファイルに対して、指定フォルダ以下のものをすべて取得し、これらの数(連番)に抜けがないかをチェックするAppleScriptです。

filenames2.png

指定フォルダ以下に存在するテキストファイルをSpotlightで検索し、すべてのファイル名を取得してそこから先頭に存在する1〜3桁の数字を取得。全角文字が混入している場合にそなえて数字をすべて全角→半角変換。これらの数(おそらく連番)で途中に抜けがないかチェックします。

実行にはShane StanleyのAppleScript Libraries「BridgePlus」「MetaData Lib」のインストールが必要です。

連番リストの作成や全角→半角変換、Spotlight検索などをScript Librariesの機能に依存して書いています。また、集合(NSMutableSet)を扱えることで非常に高度な処理を完結に記述できています。処理速度も非常に高速です。

 127ファイルの連番チェック処理:0.113457024097 sec.
 449ファイルの連番チェック処理:0.381642997265 sec.

(結果はMacBook Pro Retina 2012 Core i7 2.66GHz+8GBでの所用時間)

AppleScript名:指定フォルダ以下のテキストファイルのファイル名冒頭についている数字から欠番を求める
– Created 2017-09-04 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use mdLib : script “Metadata Lib” version “1.0.0″
use bPlus : script “BridgePlus”
–http://piyocast.com/as/archives/4803

property SMSForder : a reference to current application’s SMSForder
property NSMutableArray : a reference to current application’s NSMutableArray
property NSMutableSet : a reference to current application’s NSMutableSet
property NSIndexSet : a reference to current application’s NSIndexSet
property NSRegularExpressionDotMatchesLineSeparators : a reference to current application’s NSRegularExpressionDotMatchesLineSeparators
property NSRegularExpressionAnchorsMatchLines : a reference to current application’s NSRegularExpressionAnchorsMatchLines
property NSRegularExpression : a reference to current application’s NSRegularExpression
property NSString : a reference to current application’s NSString

load framework –BridgePlus’s force framework loading command

–選択したフォルダ以下のPlain TextをすべてSpotlightで求める(POSIX path list)
set theFolder to (choose folder)
set aRes to retMissingNumberFromEachFiles(theFolder, {“public.plain-text”}) of me
–>  {}

on retMissingNumberFromEachFiles(theFolder, fileTypeList)
  set theFiles to mdLib’s searchFolders:{theFolder} searchString:“kMDItemContentType IN[c] %@” searchArgs:fileTypeList
  
if theFiles = {} then return
  
  
–取得したPOSIX Pathのリストからファイル名の部分のみ抽出
  
set anArray to NSMutableArray’s arrayWithArray:theFiles
  
set bArray to (anArray’s valueForKeyPath:“lastPathComponent”) as list
  
  
–各ファイルの名称の冒頭から1〜3桁 の数字を取り出して、全角–>半角変換を行いつつリストに追加
  
set nArray to NSMutableArray’s new()
  
repeat with i in bArray
    set j to contents of i
    
set aRes to (my findPattern:“^\\d{1,3}” inString:j)
    
if aRes is not equal to {} then
      set jj to (contents of first item of aRes) as string
      
set jj2 to (SMSForder’s transformedFrom:jj ICUTransform:“Fullwidth-Halfwidth” inverse:false) as integer
      (
nArray’s addObject:jj2)
    end if
  end repeat
  
  
–最大値、最小値をもとに連番リストを作成し、ファイル名から得られた配列データとの補集合を求める
  
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 retMissingNumberFromEachFiles

on findPattern:thePattern inString:theString
  set theOptions to ((NSRegularExpressionDotMatchesLineSeparators) as integer) + ((NSRegularExpressionAnchorsMatchLines) as integer)
  
set theRegEx to NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
  
set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString}
  
set theFinds to theFinds as list
  
set theResult to {}
  
set theNSString to NSString’s stringWithString:theString
  
  
repeat with i in theFinds
    set theRange to (contents of i)’s range()
    
set end of theResult to (theNSString’s substringWithRange:theRange) as string
  end repeat
  
return theResult
end findPattern:inString:

★Click Here to Open This Script