Menu

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

AppleScriptの穴

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

タグ: 10.13savvy

TerminalですべてのテーマのWindowを新規作成する

Posted on 4月 15, 2019 by Takaaki Naganoya

Terminal.appで実行環境に登録されているすべてのTerminalのテーマのWindowを新規オープンするAppleScriptです。

実行環境のTerminal.appに登録されている「プロファイル」をリストアップし、各プロファイル(AppleScript用語辞書的には「settings set」)で新規Windowを作成します。

本プログラムは、Terminal.appの機能確認のためのものであって、これ自体に意味はありません。

AppleScript名:TerminalですべてのテーマのWindowを新規作成する
— Created 2019-04-14 by Takaaki Naganoya
— 2019 Piyomaru Software

tell application "Terminal"
  set nList to name of every settings set
  
  
repeat with i in nList
    set j to contents of i
    
changeSettingAndMakeNewWindow(j) of me
  end repeat
end tell

on changeSettingAndMakeNewWindow(aTheme as string)
  tell application "Terminal"
    set tmpID to settings set aTheme
    
set default settings to tmpID
    
set startup settings to tmpID
    
    
tell window 1
      do script ""
      
activate
    end tell
  end tell
end changeSettingAndMakeNewWindow

★Click Here to Open This Script 

Posted in How To | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Terminal | Leave a comment

指定MIME TypeのUTI文字列などを取得する

Posted on 4月 14, 2019 by Takaaki Naganoya

指定MIME TypeからUTIの文字列などのさまざまな情報を取得するAppleScriptです。

漠然と身の回りにファイル拡張子、UTI、MIME-typeなどの情報が転がっていますが、それぞれの意味を考え直すと、向き不向きというか想定されている利用シーンなどが大幅に異なります。

種類 内容説明
ファイル拡張子 ファイルの形式を一意に特定するもの。ただし、「.jpg」「.jpeg」など若干の表記ゆらぎを許容。詐称できてしまう
UTI ファイルのmacOS上での形式を一意に特定するもの。OS側が判断するものだが、拡張子次第。ただし、ゆらぎがない。UTI同士は階層構造を形成しており、上位のUTI(public.image)を指定することで下位UTIもまとめて指定できたりする
MIME-type 当該のファイルがインターネット上のファイル交換時にどのように受け取られるかを示す識別子。AppleScriptバンドルは拡張子「.scptd」で、UTIは「com.apple.applescript.script-bundle」だが、MIME-typeを取得すると「inode/directory」となる

と、ずいぶん違います。指定のファイルからMIME-typeを取得するだけであれば、こんな感じでしょうか。ただ、拡張子からMIME-typeを取得したり、MIME-typeからUTIを求めたりと、相互に変換する場合には(変換に意味がある場合には)いろいろと道具が必要です。

AppleScript名:指定ファイルからMIME-typeを取得する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/04/14
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

set aFile to POSIX path of (choose file)
set mRes to do shell script "/usr/bin/file -b –mime-type " & quoted form of aFile
–> "text/html"

★Click Here to Open This Script 

UTI.frameworkを用いる場合にはこちら。

UTI.frameworkをビルドして使用しています。macOS 10.11/10.12/10.13では~/Library/Frameworksにインストールしてスクリプトエディタ上で本Scriptを実行すると記述どおりの結果が得られます。

macOS 10.14ではScript Debuggerを使用するか、あるいはSIPを解除してスクリプトエディタ上で実行することが可能です。自分でビルドして自分でCode Signしたコードぐらい、ホームディレクトリ下で勝手に実行させてほしいものです。

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

AppleScript名:指定MIME TypeのUTI文字列などを取得する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/04/12
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "UTI" –https://github.com/macmade/objc-uti
use scripting additions

set aUTIObj to (current application’s UTI’s UTIWithMIMEType:"image/gif")

set aRes to aUTIObj’s identifier()
–> com.compuserve.gif

set aRes to aUTIObj’s |description|() as string
–> "Graphics Interchange Format(GIF)"

set aRes to aUTIObj’s preferredOSType() as string
–> "GIFf"

set aRes to aUTIObj’s isDynamic() as boolean
–> false

set aRes to aUTIObj’s preferredFilenameExtension() as string
–> "gif"

set aRes to aUTIObj’s preferredMIMEType() as string
–> "image/gif"

set aRes to aUTIObj’s preferredNSPboardType() as string
–> "missing value"

set aRes to aUTIObj’s declaration() as record
–> {UTTypeIdentifier:"com.compuserve.gif", UTTypeDescription:"Graphics Interchange Format (GIF)", UTTypeTagSpecification:{|com.apple.ostype|:{"GIFf"}, |public.mime-type|:{"image/gif"}, |public.filename-extension|:{"gif"}}, UTTypeConformsTo:{"public.image"}}

set aRes to aUTIObj’s declaringBundleURL()’s |path|() as string
–> "/System/Library/CoreServices/CoreTypes.bundle"

set aRes to aUTIObj’s tagSpecifications() as record
–> {|com.apple.ostype|:{"GIFf"}, |public.mime-type|:{"image/gif"}, |public.filename-extension|:{"gif"}}

set aRes to aUTIObj’s conformsTo() as list
–> {"public.image"}

set aRes to aUTIObj’s referenceURL()
–> missing value

set aRes to aUTIObj’s |version|()
–> missing value

★Click Here to Open This Script 

Posted in file MIME UTI | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy | Leave a comment

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

Posted on 4月 13, 2019 by Takaaki Naganoya

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

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

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

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

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

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

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

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

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

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

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

★Click Here to Open This Script 

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

指定フォルダ内のファイルのうち、指定拡張子リストに入っているものをすべて取得してフルパスを返す

Posted on 4月 12, 2019 by Takaaki Naganoya

指定フォルダに入っているファイルのうち、指定拡張子リストに該当するものをフルパスで返すAppleScriptです。

macOS 10.6以降の64ビット化されたFinderは動作が遅いため、大量のファイル情報を取得するなどの用途には不向きです。このため、「(ファイル選択を取得する以外の)ファイル処理にはFinderを使わない」のが現在のAppleScriptのセオリーとなっています。

本Scriptでは、~/Library/Script Libraries/フォルダの存在確認と、その中に格納されている「scpt」「scptd」の拡張子を持つAppleScript書類ファイルのリストアップを高速に行います。

AppleScript名:指定フォルダ内のファイルのうち、指定拡張子リストに入っているものをすべて取得してフルパスを返す
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

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

set anExpList to {"scpt", "scptd"} — no dot
set libPath to (((path to library folder from user domain) as string) & "Script Libraries:")
set posixLibPath to POSIX path of libPath
set fList to getFilepathListByExt(posixLibPath, anExpList) of me

on getFilepathListByExt(aFol, aExtList as list)
  set aFM to NSFileManager’s defaultManager()
  
set aFolExt to (aFM’s fileExistsAtPath:aFol isDirectory:true) as boolean
  
if aFolExt = false then return {} –フォルダ自体が存在しなければヌルリストを返す
  
  
set aURL to |NSURL|’s fileURLWithPath:aFol
  
set theOptions to ((current application’s NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((current application’s NSDirectoryEnumerationSkipsHiddenFiles) as integer)
  
set urlArray to aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value)
  
set thePred to NSPredicate’s predicateWithFormat_("pathExtension.lowercaseString IN %@", aExtList)
  
set anArray to (urlArray’s filteredArrayUsingPredicate:thePred)
  
return anArray as {missing value, list}
end getFilepathListByExt

★Click Here to Open This Script 

Posted in file list | Tagged 10.12savvy 10.13savvy 10.14savvy NSFileManager NSPredicate NSURL | Leave a comment

PDFのしおり(TOC)の内容を取得する v3

Posted on 4月 11, 2019 by Takaaki Naganoya

指定のTOCつきPDFからTOCをrecord in listで取得するAppleScriptです。各TOC項目のページ数、アウトラインレベル、ラベル(ページ名称)を順次取得して配列に出力します。

先日発売したBlogアーカイブ本のTOCを本Scriptで取得すると、以下のようになります(抜粋)。

{{pageIndex:1, outlineLevel:1, outLabel:"表紙"}, {pageIndex:2, outlineLevel:1, outLabel:"商標について"}, {pageIndex:3, outlineLevel:1, outLabel:"本書をご覧になるために"}, ... {pageIndex:34, outlineLevel:1, outLabel:"2011/1"}, {pageIndex:35, outlineLevel:2, outLabel:"リスト同士のdiffをとる"}, {pageIndex:40, outlineLevel:2, outLabel:"日本の月呼称を返す"}, {pageIndex:42, outlineLevel:2, outLabel:"指定の月を1月分リスト化"},...... {pageIndex:450, outlineLevel:1, outLabel:"奥付"}}

あれ? 「本書について」(2P)という記事が掲載もれしているのを、TOC出力から見つけてしまった、、、、、、、、

AppleScript名:PDFのしおり(TOC)の内容を取得する v3
— Created 2017-01-09 by Takaaki Naganoya
— Modified 2019-04-10 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "Quartz"

property |NSURL| : a reference to current application’s |NSURL|
property PDFDocument : a reference to current application’s PDFDocument
property titleList : {}

set my titleList to {}

set aFile to POSIX path of (choose file of type {"com.adobe.pdf"})

set fileURL to |NSURL|’s fileURLWithPath:aFile
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:fileURL

–TOCの読み込み
set parentOL to aPDFdoc’s outlineRoot() –あらかじめTOCが存在していないとmissing valueになる
if parentOL is equal to missing value then
  display dialog "本PDFにはTOCが添付されていないため、処理を終了します" with title "No TOC Error:"
  
return
end if

getChilds(parentOL) of me
return (my titleList)
–> {{pageIndex:1, outlineLevel:1, outLabel:"表紙"}, {pageIndex:2, outlineLevel:1, outLabel:"商標について"}, {pageIndex:3, outlineLevel:1, outLabel:"本書をご覧になるために"}, {pageIndex:5, outlineLevel:1, outLabel:"まえがき"},…… {pageIndex:33, outlineLevel:1, outLabel:"2011年"}, {pageIndex:34, outlineLevel:1, outLabel:"2011/1"}, {pageIndex:35, outlineLevel:2, outLabel:"リスト同士のdiffをとる"}, {pageIndex:40, outlineLevel:2, outLabel:"日本の月呼称を返す"},…….}

–PDFOutlineを再帰で取得する
on getChilds(parentOL)
  set outLineStr to parentOL’s label()
  
set outLineCount to (parentOL’s numberOfChildren()) as number
  
  
repeat with i from 0 to (outLineCount – 1)
    set anOut to (parentOL’s childAtIndex:i)
    
    
–Label (Page Title)
    
set tmpOut to (anOut’s label()) as string
    
    
–Page Indel Label
    
set tmpInd to (anOut’s destination()’s page()’s label()) as integer
    
    
–Outline Level
    
set outlevel to 0
    
copy anOut to aTmpOut
    
repeat
      set aTmpOut to aTmpOut’s |parent|()
      
if aTmpOut = missing value then exit repeat
      
set outlevel to outlevel + 1
    end repeat
    
    
set the end of my titleList to {pageIndex:tmpInd, outlineLevel:outlevel, outLabel:tmpOut}
    
    
set tmpChild to (anOut’s numberOfChildren()) as integer
    
    
if tmpChild is not equal to 0 then
      getChilds(anOut) of me
    end if
  end repeat
end getChilds

★Click Here to Open This Script 

Posted in list PDF Record recursive call | Tagged 10.12savvy 10.13savvy 10.14savvy NSURL PDFDocument | Leave a comment

PDFのしおり(TOC)の内容を取得する v2

Posted on 4月 10, 2019 by Takaaki Naganoya

指定のTOCつきPDFからTOCを1Dリスト(1次元配列)で取得するAppleScriptです。各TOC項目のlabelの文字列を順次取得して1次元配列に出力します。

AppleScript名:PDFのしおり(TOC)の内容を取得する v2
— Created 2017-01-09 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "Quartz"

property |NSURL| : a reference to current application’s |NSURL|
property PDFDocument : a reference to current application’s PDFDocument
property titleList : {}

set my titleList to {}

set aFile to POSIX path of (choose file of type {"com.adobe.pdf"})

set fileURL to |NSURL|’s fileURLWithPath:aFile
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:fileURL

–TOCの読み込み
set parentOL to aPDFdoc’s outlineRoot() –あらかじめTOCが存在していないとmissing valueになる
if parentOL is equal to missing value then
  display dialog "本PDFにはTOCが添付されていないため、処理を終了します" with title "No TOC Error:"
  
return
end if

getChilds(parentOL) of me
return (my titleList)

–再帰処理してみた
on getChilds(parentOL)
  set outLineStr to parentOL’s label()
  
set outLineCount to (parentOL’s numberOfChildren()) as number
  
  
repeat with i from 0 to (outLineCount – 1)
    set anOut to (parentOL’s childAtIndex:i)
    
set tmpOut to (anOut’s label()) as string
    
set the end of my titleList to tmpOut
    
set tmpChild to (anOut’s numberOfChildren()) as integer
    
    
if tmpChild is not equal to 0 then
      getChilds(anOut) of me
    end if
  end repeat
end getChilds

★Click Here to Open This Script 

Posted in list PDF recursive call | Tagged 10.12savvy 10.13savvy 10.14savvy NSURL PDFDocument | Leave a comment

Appleがいまだに直さないPDFViewのバグを回避する

Posted on 4月 10, 2019 by Takaaki Naganoya

PDFViewからcurrentPage()が取れないという、最低最悪レベルのバグが放置されたOSがリリースされ続けて2年。Devlopper Supportに報告しても返事の1つもない今日このごろです。

Apple Developper Connectionのインシデントを消費して質問しても返答がないので、本当に頭にきています。

この、macOS 10.13のBetaでは存在しなかったのにRelease版で作られた画期的なバグを回避するために、いろいろ工夫をしてみました。

テストしてみたところ、Objective-CやSwiftでは発生していないため、Scripting Bridgeのみで発生しているらしきバグのようです。こんな基本的な箇所でバグを作って直さない連中の気が知れません。

AppleのDevelopper Supportが役立たずで仕事をしないのは今日にはじまった話ではないので(Mailing Listが落ちた状態で、報告しても1か月放置した事件など)、仕方なく回避策を検討してみました。

(1)PDFViewまわりのWrapping ClassをObjective-Cで書いて使う
(2)Objective-Cでユーティリティを書いて、パラメータとして与えたPDFViewからObjective-CでcurrentPageを取得する
(3)今後もAppleがバグを作り続けることが予想されるため、サードパーティのPSPDFKitを導入する

といった対策を検討していたのですが、最も手短なものとして、

(4)表示中のページを自前で管理して、PDFViewに指定ページの内容を随時切り出して表示する

という対処を行ってみました。

PDFをオープンしてPDFViewで表示させ、左右の矢印キーでページをめくることができます。なお、macOS 10.14.5beta+Xcode 10.2上でビルドと確認を行いましたが、内容的にはXcodeのバージョンは関係ありません。

–> Download Xcode Project Archive (pdfTestZ)

AppleScript名:AppDelegate.applescript
—
— AppDelegate.applescript
— pdfTestZ
—
— Created by Takaaki Naganoya on 2019/04/09.
— Copyright © 2019 Piyomaru Software. All rights reserved.
—

script AppDelegate
  property parent : class "NSObject"
  
  
— IBOutlets
  
property theWindow : missing value
  
property aView : missing value
  
property maxPageNum : 0
  
property curPageIndex : 0
  
property aPDFDoc : missing value
  
property pLabelField : missing value
  
  
  
on applicationWillFinishLaunching:aNotification
  
end applicationWillFinishLaunching:
  
  
on applicationShouldTerminate:sender
    return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
  
on clicked:aSender
    set aTag to (tag of aSender) as integer
    
    
if aTag = 100 then
      set aPath to choose file of type {"com.adobe.pdf"}
      
      
set pPath to POSIX path of aPath
      
set aURL to current application’s |NSURL|’s fileURLWithPath:pPath
      
set my aPDFDoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL
      
      
set myFileName to (current application’s NSString’s stringWithString:pPath)’s lastPathComponent()
      
theWindow’s setTitle:myFileName
      
      
set maxPageNum to ((aPDFDoc’s pageCount()) as integer) – 1
      
set curPageIndex to 0
      
      
log {"maxPageNum", maxPageNum}
      
      
aView’s setAutoScales:true
      
aView’s setDisplaysPageBreaks:true
      
      
my dispCurPageDoc()
      
set tmpStr to ((maxPageNum + 1) as string) & "/" & ((curPageIndex + 1) as string)
      
pLabelField’s setStringValue:tmpStr
      
    else if aTag = 200 then
      
      
set tmpPage to my (aPDFDoc’s pageAtIndex:(curPageIndex))
      
set curPageLabel to tmpPage’s label()
      
      
tell current application
        display dialog (curPageLabel as string)
      end tell
      
    else if aTag = 1000 then
      my decPage()
      
my dispCurPageDoc()
      
    else if aTag = 1010 then
      my incPage()
      
my dispCurPageDoc()
      
    end if
    
  end clicked:
  
  
on dispCurPageDoc()
    set tmpPage to my (aPDFDoc’s pageAtIndex:(curPageIndex))
    
set tmpDoc to (current application’s PDFDocument’s alloc()’s initWithData:(tmpPage’s dataRepresentation()))
    
aView’s setDocument:tmpDoc
    
    
set tmpStr to ((maxPageNum + 1) as string) & "/" & ((curPageIndex + 1) as string)
    
pLabelField’s setStringValue:tmpStr
  end dispCurPageDoc
  
  
  
on incPage()
    if curPageIndex = maxPageNum then
      –何もしない
    else if curPageIndex < maxPageNum then
      set curPageIndex to curPageIndex + 1
    end if
  end incPage
  
  
on decPage()
    if curPageIndex = 0 then
      –何もしない
    else if curPageIndex > 0 then
      set curPageIndex to curPageIndex – 1
    end if
  end decPage
end script

★Click Here to Open This Script 

Posted in AppleScript Application on Xcode PDF | Tagged 10.13savvy 10.14savvy NSString NSURL PDFDocument PDFPage | 1 Comment

マウスカーソルを隠す→表示する

Posted on 4月 9, 2019 by Takaaki Naganoya

マウスカーソルを非表示、表示にするAppleScriptです。

マウスカーソルの変更はClassic Mac OSの時代にそういうOSAXを見かけた覚えがありますが、今日びCocoaの機能を用いて割と簡単に表示/非表示の切り替えができることに気づきました。

# カーソルのイメージを変更するのには失敗しました

長時間のバッチ(一括)処理中にユーザーに操作してほしくない場合には、デスクトップ機であればマウスを取り外しておく措置を講じたこともありますが、さらに念には念のためにこうしてマウスカーソルを非表示にしておくのもよいかもしれません。

MacBook Proなどのノートでバッチ処理を行いつつユーザーの操作により処理が妨げられることを防ぐためにカーソル非表示にするのも、(ユーザーが不安に感じなければ)案外いい手段でしょう。

AppleScript的には、意外と使い勝手がありそうです。

AppleScript名:マウスカーソルを隠す→表示する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/04/09
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set curCursor to current application’s NSCursor’s currentCursor()

current application’s NSCursor’s hide()

delay 5

current application’s NSCursor’s unhide()

★Click Here to Open This Script 

Posted in System | Tagged 10.12savvy 10.13savvy 10.14savvy NSCursor | Leave a comment

ASの実行結果をNumbersの表に出力する

Posted on 4月 5, 2019 by Takaaki Naganoya

文字列で与えたAppleScriptを実行し、その実行結果をテキストで取得したうえにラベルと値に分割してNumbersの「表」に組み立てるAppleScriptです。

仕様書の作成とかテストデータの評価とかそういう仕事を行うさいに、プログラムコード(AppleScriptが出力するrecordとかlistとか)が読めない人向けに確認してもらうために作成したものです。

Numbersに出力すること自体にはさほど意味はありませんが(ExcelでもREST API経由でGoogle Spread Sheetに出力したっていいわけで)、とりあえずプログラムの結果出力になじみのない方々に計算結果を見ていただくためのものです。

AppleScriptの実行結果をスクリプトエディタではなくOSAScriptControllerなどから受け取る部品などが揃ってきたので、こういう加工も割とすぐにできていい感じです。

ちなみに、Numbersの表のセルに対して直接AppleScriptからデータを突っ込んでいますが、これはあらかじめ出力するデータ数が少ないことが見込まれているためです。多い場合(100件前後がひとつの基準に)には、CSVファイルに書き出してNumbersにオープンさせることになるでしょう。

例によって、Numbersの表セルに対してデータを突っ込むさいには非同期モードで実行して速度を稼いでいます。

AppleScript名:ASの実行結果をNumbersの表に出力する
— Created 2019/04/05 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "OSAKit"
use framework "AppKit"
use bLus : script "BridgePlus"

property NSString : a reference to current application’s NSString
property NSThread : a reference to current application’s NSThread
property OSAScript : a reference to current application’s OSAScript
property NSTextView : a reference to current application’s NSTextView
property OSAScriptView : a reference to current application’s OSAScriptView
property OSAScriptController : a reference to current application’s OSAScriptController

property SMSForder : a reference to current application’s SMSForder

property theResult : "" –result

set origStr to "
tell application \"Numbers\"
  tell front document
    properties
  end tell
end tell
"

my performSelectorOnMainThread:"execASandReturnString:" withObject:origStr waitUntilDone:true
set aStr to (my theResult)

set aList to my getListFromText:aStr

makeNewNumbersDocumentAndTable(length of aList, 2) of me
fillCurrentTable(aList) of me

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

–リストに入れたレコードを、指定の属性ラベルの値で抽出。値が別途指定のリストの中に存在していることが条件
on filterRecList:(aRecList as list) byLabel:(aPredicate as string) andSublist:(aSubList as list)
  –ListからNSArrayへの型変換
  
set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aSubArray to current application’s NSArray’s arrayWithArray:aSubList
  
  
–抽出
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(aPredicate, aSubArray)
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
  
–NSArrayからListに型変換して返す
  
set bList to filteredArray as list
  
return bList
end filterRecList:byLabel:andSublist:

–listの共通項を返す
on getSameItemsInLists:(aList as list) withList:(bList as list)
  
  
–ASオブジェクトをCocoaオブジェクトに変換
  
set aArray to current application’s NSArray’s arrayWithArray:aList
  
set bArray to current application’s NSArray’s arrayWithArray:bList
  
  
— まとめる
  
set allSet to current application’s NSMutableSet’s setWithArray:aArray
  
allSet’s addObjectsFromArray:bArray
  
  
–重複する要素のみ抜き出す
  
set duplicateSet to current application’s NSMutableSet’s setWithArray:aArray
  
duplicateSet’s intersectSet:(current application’s NSSet’s setWithArray:bArray)
  
  
–重複部分だけを返す
  
set resArray to duplicateSet’s allObjects()
  
  
set resList to resArray as list
  
  
return resList
end getSameItemsInLists:withList:

–スクリプトエディタのresult欄に返ってきたテキストをリストに変える
on getListFromText:aText
  
  
script getListFromTextO
    property aaText : ""
    
property gList : {}
    
property outList : {}
    
property aG : ""
    
property valList : {}
  end script
  
  
copy aText to (aaText of getListFromTextO)
  
  
set (gList of getListFromTextO) to {}
  
set (outList of getListFromTextO) to {}
  
set (aG of getListFromTextO) to ""
  
set (valList of getListFromTextO) to {}
  
  
if (aaText of getListFromTextO) does not start with "{" and (aaText of getListFromTextO) does not end with "}" then
    return {}
  end if
  
  
set aLen to length of (aaText of getListFromTextO)
  
set (aG of getListFromTextO) to text 2 thru -2 of (aaText of getListFromTextO)
  
set (gList of getListFromTextO) to characters of (aG of getListFromTextO)
  
  
  
set sPos to 2 –1文字目は\"{\"なので2文字目からスキャンを開始する
  
set ePos to 2
  
  
set imdF to false –Immediate Data Flag(文字列中を示すダブルクォート内の場合にはtrueになる)
  
set listF to 0 –stacking段数が入る
  
  
set attrF to true –属性ラベルスキャン時にtrue、データ末尾スキャン時にfalse
  
  
  
repeat with i in (gList of getListFromTextO)
    
    
set j to contents of i
    
    
if attrF = true and imdF = false and listF = 0 then
      
      
–属性値部分の末尾検出
      
if j = ":" then
        if text sPos thru sPos of (aaText of getListFromTextO) = " " then
          set sPos to sPos + 1
        end if
        
set anOut to text sPos thru ePos of (aaText of getListFromTextO)
        
set sPos to ePos + 1
        
set the end of (valList of getListFromTextO) to anOut
        
set attrF to false –データのスキャンを開始する
        
set imdF to false
        
set listF to 0
      end if
      
    else if imdF = false and listF = 0 and j = "," then
      
      
–データ部分の末尾検出
      
set anOut to text sPos thru (ePos – 1) of (aaText of getListFromTextO)
      
set sPos to ePos + 1
      
set the end of (valList of getListFromTextO) to anOut
      
set the end of (outList of getListFromTextO) to (valList of getListFromTextO)
      
set (valList of getListFromTextO) to {}
      
      
set attrF to true –次が属性値ラベルであることを宣言
      
set imdF to false
      
set listF to 0
      
    else if j = "{" then
      if imdF = false then
        set listF to listF + 1 –1段スタックにpush
      end if
    else if j = "}" then
      if imdF = false then
        set listF to listF – 1 –1段スタックからpop
      end if
    else if j = "\"" then
      if imdF = true then
        set imdF to false
      else
        set imdF to true
      end if
    end if
    
    
set ePos to ePos + 1
    
  end repeat
  
  
–ラストのデータ部分を出力
  
try
    set the end of (valList of getListFromTextO) to text sPos thru (ePos – 1) of (aaText of getListFromTextO)
    
set the end of (outList of getListFromTextO) to (valList of getListFromTextO)
  on error
    return false
  end try
  
  
return contents of (outList of getListFromTextO)
end getListFromText:

on execASandReturnString:(srcStr as string)
  
  
set targX to 500 –View Width
  
set targY to 200 –View Height
  
  
  
if srcStr = missing value or srcStr = "" then
    –Error
    
display dialog "Error in reading script source…." buttons {"OK"} default button 1 with icon 1
    
return
  end if
  
  
–Make AppleScript Controller & Script Editor View
  
set osaCon to OSAScriptController’s alloc()’s init()
  
set osaView to OSAScriptView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
  
–Make Result View
  
set resView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
resView’s setRichText:true
  
resView’s useAllLigatures:true
  
  
–Connect OSAScriptController to Editor View & Result View
  
osaCon’s setScriptView:osaView
  
osaCon’s setResultView:resView
  
  
–Set AppleScript Source to Editor View & Execute it
  
osaView’s setString:srcStr
  
osaCon’s runScript:(missing value)
  
  
–Get AppleScript’s Result as string
  
set aRes to resView’s |string|() as list of string or string –as anything
  
  
set my theResult to aRes –Return the result as string
  
end execASandReturnString:

on fillCurrentTable(aList)
  set aLen to length of aList
  
set aWidth to length of first item of aList
  
  
tell application "Numbers"
    tell front document
      tell active sheet
        tell table 1
          repeat with i from 1 to aLen
            tell row (i + 1)
              set aRowList to contents of item i of aList
              
repeat with ii from 1 to aWidth
                tell cell ii
                  set aTmpData to contents of item ii of aRowList
                  
ignoring application responses
                    set value to aTmpData
                  end ignoring
                end tell
              end repeat
            end tell
          end repeat
        end tell
      end tell
    end tell
  end tell
end fillCurrentTable

on makeNewNumbersDocumentAndTable(aHeight, aWidth)
  tell application "Numbers"
    make new document
    
    
tell front document
      tell active sheet
        delete every table
      end tell
    end tell
    
    
tell front document
      tell active sheet
        set tRes to make new table with properties {row count:aHeight, column count:aWidth}
        
return tRes
      end tell
    end tell
  end tell
end makeNewNumbersDocumentAndTable

★Click Here to Open This Script 

Posted in OSA Record | Tagged 10.12savvy 10.13savvy 10.14savvy NSString NSTextView NSThread Numbers OSAScript OSAScriptController OSAScriptView | Leave a comment

Keynoteのテキストアイテム内の文字の実際の幅でリサイズ

Posted on 4月 5, 2019 by Takaaki Naganoya

Keynote書類上のテキストアイテムを、内容の文字の実際の幅でリサイズするAppleScriptです。


▲テキストアイテム中のテキストをコピー(Command-C)


▲リサイズ前のテキストアイテム。文字に対してフレームサイズが大きい


▲本Scriptによりリサイズした後のテキストアイテム。文字の内容に対してフレームサイズがフィットしている


▲本Scriptにより順次複数のテキストアイテムを文字幅にリサイズしたところ。selectionがまともに動作していれば、複数のテキストアイテムを選択した状態でScriptを走らせて一度にリサイズできる(はず)だが、selectionが動作していないので複数のオブジェクトに対して同じ回数の操作が必要

Keynoteにはversion 9.0で「selection」の予約語が追加されましたが、これは一般に期待されるような「表示中のスライド上で選択中のオブジェクトへの参照が取得できる」というものではありません。

つまり、選択中のオブジェクトへの参照をKeynoteに対して問い合わせることは、(現時点では)不可能です。

そこで、テキストアイテム内の内容(object text)をコピー(Command-C)してクリップボードに格納し、その内容をキーにして現在表示中のスライド内のテキストアイテムを抽出してみたところ、うまくいきました(なんでこんなことさせられてるんだろ?)。早くselectionがKeynote上でもまともに使えるようになってほしいものです(Pages上ではけっこういい感じに使えるのに)。

テキストアイテムの特定ができるようになったので、入っているobject textのサイズに合わせてテキストアイテムをリサイズする処理を書いてみました。

クリップボードの内容をNSAttributedStringとして取得し、その画面上の描画サイズを取得するサブルーチンは毎日さんざん使っているので(掲載リストのproperty宣言部をソートするのに使っています)信頼性があります。

とりあえず、擬似的に現在選択中のオブジェクトを特定する実験コードにちょっとだけ機能をつけたものですが、操作1に対して結果1というのは普通「自動化」とか言わないので、実用性は皆無です(めんどくさい操作ステップを省けるぐらいしか)。

AppleScript名:Keynoteのテキストアイテム内の文字の実際の幅でリサイズ
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/04/05
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use BPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

property NSFont : a reference to current application’s NSFont
property NSColor : a reference to current application’s NSColor
property NSPasteboard : a reference to current application’s NSPasteboard
property NSAttributedString : a reference to current application’s NSAttributedString
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName

load framework

–クリップボードの内容をテキストとして取得し、現在のKeynote書類中のtext itemのうちコピーしたテキストに該当するものを抽出
set targString to (the clipboard) as string

tell application "Keynote"
  tell document 1
    tell current slide
      set tList to every text item whose object text is targString
      
if tList = {} then return
      
set targObject to first item of tList
    end tell
  end tell
end tell

–クリップボードの内容をNSAttributedStringに
set anAttr to my getClipboardASStyledText()

–Split Attributed Strings into lines
set attrList to splitAttributedStringByLines(anAttr) of me

–Sort Attributed String list by its width in descending (large–> small)
set aResList to shellSortListDecending(attrList, 1) of me
set {targObjWidth, targAttrStrObj} to first item of aResList

–Resize Text item objet’s frame by its drawing width on screen
tell application "Keynote"
  tell document 1
    tell current slide
      tell targObject
        set width to ((targObjWidth as number) + 10) –resize it !
      end tell
    end tell
  end tell
end tell

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

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

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

— クリップボードの内容をNSAttributedStringとして取り出して返す
on getClipboardASStyledText()
  try
    set theNSPasteboard to NSPasteboard’s generalPasteboard()
    
set theAttributedStringNSArray to theNSPasteboard’s readObjectsForClasses:({NSAttributedString}) options:(missing value)
    
set theNSAttributedString to theAttributedStringNSArray’s objectAtIndex:0
    
return theNSAttributedString
  on error
    return missing value
  end try
end getClipboardASStyledText

on splitAttributedStringByLines(theStyledText)
  set outList to {}
  
set outAttr to NSMutableAttributedString’s alloc()’s initWithString:""
  
  
set thePureString to theStyledText’s |string|() –pure string from theStyledText
  
set theLength to theStyledText’s |length|()
  
set startIndex to 0
  
  
repeat until (startIndex = theLength)
    set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(reference) inRange:{startIndex, theLength – startIndex}
    
set aText to (thePureString’s substringWithRange:theRange) as string –String
    
    
set aColor to (theAtts’s valueForKeyPath:"NSColor") –Color
    
set aFont to (theAtts’s valueForKeyPath:"NSFont") –Font
    
if aFont is equal to missing value then error "Not font name and size are specified" –Font Name error
    
set aDFontName to aFont’s displayName() –Font Name
    
set aDFontSize to aFont’s pointSize() –Font Size
    
    
set tmpAttrStr to generateAttributedString(aText, aDFontName, aDFontSize, aColor) of me
    
outAttr’s appendAttributedString:tmpAttrStr
    
    
if (aText contains return) or (aText contains string id 10) then –CR or LF
      set tmpSize to outAttr’s |size|()
      
set tmpWidth to retWidthFromSize(tmpSize) of me
      
      
set the end of outList to {tmpWidth, outAttr}
      
set outAttr to NSMutableAttributedString’s alloc()’s initWithString:""
    end if
    
    
set startIndex to current application’s NSMaxRange(theRange)
  end repeat
  
  
set tmpSize to outAttr’s |size|()
  
set tmpWidth to retWidthFromSize(tmpSize) of me
  
set the end of outList to {tmpWidth, outAttr}
  
  
return outList
end splitAttributedStringByLines

on retWidthFromSize(tmpSize)
  if class of tmpSize = record then
    –macOS 10.10, 10.11, 10.12
    
set tmpWidth to width of tmpSize
  else
    –macOS 10.13, 10.14
    
set tmpWidth to first item of tmpSize
  end if
  
return tmpWidth
end retWidthFromSize

on generateAttributedString(aStr, aFontPSName, aFontSize, aColor)
  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 addAttribute:(NSForegroundColorAttributeName) value:aColor range:aRange
  
tmpAttr’s endEditing()
  
return tmpAttr
end generateAttributedString

★Click Here to Open This Script 

Posted in Clipboard list Record RTF Sort | Tagged 10.12savvy 10.13savvy 10.14savvy Keynote NSAttributedString NSColor NSFont NSFontAttributeName NSForegroundColorAttributeName NSMutableAttributedString NSPasteboard | Leave a comment

as anything

Posted on 4月 3, 2019 by Takaaki Naganoya

as anythingについてまとまった情報がなかったので、まとめておきました。結論めいたものはとくになく、ただ、手持ちの情報を並べてみただけの内容なのでご容赦ください。「これはこうあるべき!」とかいう提言とかは一切ありません。

割と最近なas anythingとの遭遇

つい最近まで、ながらく存在自体を知らなかったのが、このanythingという予約語です。

AppleScriptの用語辞書をASObjC Explorer 4で書いて試していたとき、パラメータの型に「any」というものが存在していることには気づいていましたが、それでも「anything」という予約語があることには気づきませんでした。

本格的に知ったのは、applescript-stdlibを漁っていたときです。

同ライブラリはトリッキーなScriptの記述方法の見本市みたいになっていて、内容自体の完全理解についてはサジを投げた状態でしたが、見たことのない記述にいろいろ行き当たりました(もっと読みやすく書いてほしかった ^ー^;;)。

delegateの呼び出し先にscriptオブジェクト内部のハンドラを指定したりと、「そんな書き方できるんだ。やらんけど」と、まるで古文書をひもとくような感覚(これを見ていると、こーゆー風にblocks構文をAppleScriptでも記述できればいいのに、と思います)。

そこで見つけた「as anything」表記。これは一体なんなんでしょう?

いい加減に使えてとても便利なas anything

実際に動かしてみたところ、「あーー、この機能昔から欲しかったわー」という内容でした。anonymous classとでもいうのでしょうか。castするときにclass名を指定しないワイルドカードなclassと理解しました。

「as anything」でAppleScriptのデータ型はそのまま、AppleScriptのデータ型にcast可能なCocoaのデータはAppleScriptのデータ型に変換。変換不能なCocoaのデータ型についてはそのまま、という処理をしてくれます。

# ただし要素数が1つのNSArrayをas anythingでAppleScript Objetに変換すると、listではなく中身のデータが取り出されてしまう(型が合わなくなる)ので、注意が必要とのこと(Thanks Shane!)
# なので、あらかじめNSArrayが返ってくることがわかっている場合には、as list。NSArrayか他のデータ(missing valueとか)が返ってくる場合には、as {missing value, list} などとするとよいでしょう

BridgePlus的にいえば、「ASify without BridgePlus」といったところでしょうか。CocoaとAppleScriptの間でデータをやりとりするのにえっらく都合がよかったのです。かくして、「as anything」を愛用しまくるスタイルが確立。

ただ、anythingは言語仕様のはざまで埋もれかけていた「枝葉末節」の中の一番の「枝葉」ともいうべき、言語仕様の極北。AppleScript Language Guideにも記載されていないほどの枝葉仕様。

AppleScriptObjCのプログラム中に書くと、予約語が用意されていないためか「as anything」が「as list of string or string」などと解釈されてしまいます(動作自体はanythingと同じ)。

anythingについては、Apple側がわりとぞんざいに仕様を放置していたきらいがあります。問題が起こらないかぎり放っておこう、と。as «class isot»のようにある日突然(Mac OS X 10.5で)消えてなくなったりしないかちょっと不安に感じたこともありましたが、一応まがりなりにも予約語が割り振られているので(生Apple Eventを記述させられないので)、そんなに簡単に消えないとは思います。

主要開発環境で足並みそろわず

雑に放置していたAppleに対して、この問題に正面から向き合っていた集団が存在します。それが、Script DebuggerのメーカーであるLate Night Softwareです。

macOS 10.12や10.13上ではあいかわらずASOCのプログラムでは「as anything」が「as list of string or string」と解釈されますが、macOS 10.14で対応に差が出ました。

Script Debugger上では「anything」が「any」と解釈され、スクリプトエディタ上では「anything」と解釈されます。

macOS 10.14上でのスクリプトエディタで「as anything」が「as list of string or string」のように化けないのはいいと思います。ただ、Script DebuggerのメーカーであるLate Night Software側ではこれに「any」という別の予約語を割り振ったようで、、、、足並みがそろっていません。

一応、macOS 10.14上のスクリプトエディタv2.11上で「as anything」と記述したScriptを、同じくmacOS 10.14上のScript Debuggerに持っていくと「as any」と表示されます。いったん中間コードにコンパイルされたものは問題がないようです。

問題になるのは、Blogなどに掲載されている文字の状態のAppleScriptで、anythingについてはコンパイル(構文確認)時に気をつける必要がありそうです(メイン環境が10.14に移行していないのでちょっとまだ他人事)。

困ったときの古文書だより

一応、現代AppleScriptのルーツの資料とされている「AppleScriptLanguageGuide 1.3.7」(1999/5/5)のPDFを確認してみると、けっこう悩ましい内容が記述されています。

自分はanythingをclassか何かだと思っていたのですが、そこには定数(constants)だと書かれています。AppleScript version 1.3.7のころには、missing value(不定値)みたいな運用が行われていたようです。

AppleのEngineering Teamの見解は?

匿名希望の方がApple Engineering Teamに問い合わせしたところ「そんなマイナーな予約語は使うな」という返答であったとか。その一方でmacOS 10.14で「anything」の「list or list of string」への解釈化けが抑止されたりしているわけで、いまひとつわかりにくいというのが現状です。

Posted in OSA | Tagged 10.12savvy 10.13savvy 10.14savvy Script Debugger Script Editor | Leave a comment

MP3ファイルからMP3タグの情報を取得

Posted on 4月 1, 2019 by Takaaki Naganoya

MP3ファイルからMP3タグの情報を取得するAppleScriptです。

とりあえず「取り出せることを確認した」というレベルの実装で、取り出したデータをタグに応じた加工を行うといった処理はほとんど書いてありません。各自よろしくお取り扱いください(投げやり)。

正直、Musicフォルダ以外に転がっている野良MP3ファイルから直接MP3タグ情報を読まなくても、iTunes.appにインポートしておいて、iTunes.appやらiTunesLibrary.framework経由で楽曲ファイルにアクセスしたほうが(データ形式のゆらぎを吸収してもらえるため)安全で楽です。

AppleScript名:MP3ファイルからMP3タグの情報を取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/04/01
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
— 参照先:http://d.hatena.ne.jp/terazzo/20120107/1325967445
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use framework "AVFoundation"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSImage : a reference to current application’s NSImage
property AVURLAsset : a reference to current application’s AVURLAsset
property AVMetadataFormatID3Metadata : a reference to current application’s AVMetadataFormatID3Metadata
property AVMetadataCommonKeyAlbumName : a reference to current application’s AVMetadataCommonKeyAlbumName

set aPath to POSIX path of (choose file of type {"public.mp3"})
set aMP3Info to getMP3TagsFromFile(aPath) of me

on getMP3TagsFromFile(aPath)
  set aURL to |NSURL|’s fileURLWithPath:aPath
  
set anAsset to AVURLAsset’s assetWithURL:aURL
  
  
set aRes to (anAsset’s availableMetadataFormats()’s containsObject:(AVMetadataFormatID3Metadata)) as boolean
  
if aRes = false then return false
  
  
set origMetadata to (anAsset’s metadataForFormat:(AVMetadataFormatID3Metadata)) as list
  
set outList to {}
  
  
repeat with i in origMetadata
    set aKey to (i’s |key|()) as list of string or string –as anything
    
set bKey to i’s commonKey() as list of string or string –as anything
    
    
if aKey = "PIC" or aKey = "APIC" then
      set aVal to (i’s value())
      
set aVal to (NSImage’s alloc()’s initWithData:aVal)
    else
      set aVal to (i’s value()) as list of string or string –as anything
    end if
    
set the end of outList to {aKey, bKey, aVal}
  end repeat
  
  
return outList
end getMP3TagsFromFile

★Click Here to Open This Script 

Posted in file | Tagged 10.12savvy 10.13savvy 10.14savvy AVURLAsset NSImage NSURL | Leave a comment

FireFoxでURLをオープン(URLの間接指定)

Posted on 3月 31, 2019 by Takaaki Naganoya

FireFoxで指定URLをオープンするAppleScriptです。

FireFoxのAppleScript対応機能は、指定のURLをオープンさせるだけです。しかも、AppleScript用語辞書からアクセスできないという体たらく。2004年ごろに出回っていたサンプルScriptがそのまま最新のバージョンでも動きました。

FireFoxのAppleScript用語辞書を見てもdo javascript的な命令はないですし、ほぼ何もできない内容です(ダミーといってもいいでしょう)。なので、これをスクリプタブルだとはとても言えません。AppleScriptからコントロールする用途でFireFoxを用いることに意味はありません。

いろいろ試していたら、FireFoxが起動していない状態で本Scriptを実行すると、実行が完了しないという問題が報告されました。追試してみるとそのとおりです。あらかじめ、起動しておくなりactivateしておくなどの処理が必要です。

AppleScript名:FireFoxでURLをオープン(URLの間接指定)
set aURL to "http://piyocast.com/as"

tell application "Firefox"
  «event WWW!OURL» aURL
end tell

★Click Here to Open This Script 

Posted in Internet Raw AppleEvent Code URL | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy FireFox | Leave a comment

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

Posted on 3月 29, 2019 by Takaaki Naganoya

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

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

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

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

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

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

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

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

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

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

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

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

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

property fillCharList : {}
property fillCharCounter : 1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

★Click Here to Open This Script 

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

頭のいいタイマー割り込み実行

Posted on 3月 27, 2019 by Takaaki Naganoya

頭のいいタイマー割り込み(on idle)実行のAppleScriptを追求してみました。

AppleScript開闢(かいびゃく)以来、すでに20年以上の時間が経過しているので、on idleによるタイマー割り込み処理なんて、探せばサンプルが山のように出てくるものです。

AppleScript名:timer interrupt
property targetTime : "9:37:00"
property timerInterval : 30

on run
  set timerInterval to 30
end run

on idle
  set curTime to current date
  
set cString to time string of curTime
  
  
if cString ≥ targetTime then
    display dialog "It’s time to take off!" buttons {"OK"} default button 1 giving up after 30
    
quit
  end if
  
  
return timerInterval
end idle

★Click Here to Open This Script 

これが基礎的な内容で、このScriptをScript Editor上でアプリケーション(アプレット)形式で、「ハンドラの実行後に終了しない」をオンにして書き出すとタイマー実行アプレットが出来上がります(3分間クッキング)。

ただ、実行時刻のパラメータがプログラム内に直打ちなのが気になります。知能レベルが低い感じがします。

そこで、実行時刻のパラメータの外部供給ということを考え出すわけですが、

 (1)設定ファイルから読み込み
 (2)アプレット自身のコメント(File Comment)から読み込み
 (3)ファイル名自体から読み込み
 (4)コマンドラインから実行し、実行時にパラメータ(argv)を指定

などの方法を考えつきます(20世紀にすでにさんざんやった内容)。ただし、全角数字を半角に変換したり、ファイル名の場合には時刻セパレータの「:」がmacOS上ではファイル名に使えない文字(ディレクトリ・セパレータ)だったり、Finderが管理しているファイル名はUnicodeのNormalize方式が異なる(処理しやすいようにNormalizeし直さないとダメ)など割と頭の痛い問題がいろいろあります。

そこで利用したいのが、CocoaのDataFormatter。自然言語風に書かれた「10時41分」(全角数字入り)といった文字列から日時データをピックアップします。

そうして書いたのがこれ(↓)です。

ファイル名に書かれた時刻から実行時刻を拾ってタイマー実行します。けっこう頭がいい感じがします。

実際に、こうした処理の延長線上にTanzakuで行なっているファイル名から取得した文字列に対する形態素解析&コマンドピックアップの処理があります。

AppleScript名:10時42分
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property targTime : missing value
property timerInterval : 1

on run
  set timerInterval to 1
  
  
–Get filename from this applet
  
set myPath to path to current application
  
tell application "System Events"
    set myName to name of myPath
  end tell
  
  
–Validate filename as a natural language format date by using NSDataDetector
  
set dList to getDatesIn(myName) of me
  
repeat while dList = {}
    set myName to text returned of (display dialog "There is no time elements in my filename. Input the target time in x時xx分" default answer "午後5時45分")
    
set dList to getDatesIn(myName) of me
  end repeat
  
  
set targDate to first item of dList
  
set targTime to time string of targDate
  
display notification targTime
end run

on idle
  set curTime to current date
  
set curTimeStr to time string of curTime
  
  
if curTimeStr ≥ targTime then
    activate
    
display dialog "It’s time to take off!" buttons {"OK"} default button 1 giving up after 30
    
quit
  end if
  
  
return timerInterval
end idle

on getDatesIn(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
set {theDetector, theError} to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeDate) |error|:(reference)
  
set theMatches to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
set theResults to theMatches’s valueForKey:"date"
  
return theResults as list
end getDatesIn

★Click Here to Open This Script 

Posted in File path Natural Language Processing | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSDataDetector NSString | Leave a comment

アラートダイアログ上にsegmented controlを表示 v4

Posted on 3月 27, 2019 by Takaaki Naganoya

アラートダイアログ上にsegmented controlを複数表示して、複数選択項目を取得するAppleScriptです。

複数選択項目にpopup buttonではなくsegmented controlを使う必要性というのは、あんまりないですが………すべての選択項目が見える状態で少量の選択項目から1つを選ぶような用途には使えるのではないでしょうか。

segmented controlをそれぞれBoxに入れ、Viewにまとめ、Scroll viewに突っ込んでいますが、あまり効果があるんだかないんだか不明です。

AppleScript名:アラートダイアログ上にsegmented controlを表示 v4
— Created 2019-03-26 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
–Scroll Viewをつかってみた

property NSBox : a reference to current application’s NSBox
property NSView : a reference to current application’s NSView
property NSAlert : a reference to current application’s NSAlert
property NSScrollView : a reference to current application’s NSScrollView
property NSSegmentedControl : a reference to current application’s NSSegmentedControl
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSSegmentStyleTexturedRounded : a reference to current application’s NSSegmentStyleTexturedRounded

property returnCode : 0
property returnSels : {}

set paramObj to {myMessage:"複数項目選択", mySubMessage:"どれか選択してください。", segmentMes:{{"Red1", "Blue1", "Yellow1", "Brown1", "White1", "Cyan1", "Grey1"}, {"Red2", "Blue2", "Yellow2", "Brown2", "White2", "Cyan2", "Grey2"}, {"Red3", "Blue3", "Yellow3", "Brown3", "White3", "Cyan3", "Grey3"}, {"Red4", "Blue4", "Yellow4", "Brown4", "White4", "Cyan4", "Grey4"}, {"Red5", "Blue5", "Yellow5", "Brown5", "White5", "Cyan5", "Grey5"}}, segmentTitles:{"1st Segments", "2nd Segments", "3rd Segments", "4th Segments", "5th Segments"}}

my chooseMultipleSegments:paramObj

return my returnSels
–> {1, 2, 3, 4, 5}

on chooseMultipleSegments:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set segMes2DList to segmentMes of paramObj
  
set segTitleList to segmentTitles of paramObj
  
  
set aTmpY to (length of segMes2DList) * 60
  
  
–BoX + Segmented Control をつくる
  
set segsList to {}
  
set boxLIst to {}
  
set segsCount to 0
  
set tmpMaxX to 500
  
  
set aCount to 1
  
  
repeat with i in segMes2DList
    set aSeg to makeSegmentedControlWithStartY(i, aTmpY – segsCount – 40, 500, 40) of me
    
    
set aDBounds to aSeg’s |bounds|()
    
set tmpWidth to getWidth(aDBounds) of me
    
    
set aBox to (NSBox’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aTmpY – segsCount – 60, 500, 60)))
    (
aBox’s setTitle:(item aCount of segTitleList))
    (
aBox’s addSubview:aSeg)
    
    
if tmpWidth > tmpMaxX then set tmpMaxX to tmpWidth
    
    
set the end of segsList to aSeg –選択検出用
    
set the end of boxLIst to aBox –表示用
    
    
set segsCount to segsCount + 60
    
set aCount to aCount + 1
  end repeat
  
  
— create a view
  
set theView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, tmpMaxX, aTmpY))
  
theView’s setSubviews:boxLIst
  
  
— create a Scroll View
  
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, tmpMaxX, aTmpY))
  
aScroll’s setDocumentView:theView
  
theView’s enclosingScrollView()’s setHasHorizontalScroller:false
  
theView’s enclosingScrollView()’s setHasVerticalScroller:false
  
  
— set up alert
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aScroll
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
  
  
set my returnSels to {}
  
repeat with i in segsList
    set tmpSegSel to (i’s selectedSegment()) as number
    
set the end of (my returnSels) to tmpSegSel + 1
  end repeat
end chooseMultipleSegments:

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

on makeSegmentedControlWithStartY(titleList, aStartY, aWidth, aHeight)
  set aLen to length of titleList
  
  
set aSeg to NSSegmentedControl’s alloc()’s init()
  
aSeg’s setSegmentCount:aLen
  
  
set aCount to 0
  
repeat with i in titleList
    set j to contents of i
    (
aSeg’s setLabel:j forSegment:aCount)
    
set aCount to aCount + 1
  end repeat
  
  
aSeg’s setTranslatesAutoresizingMaskIntoConstraints:false
  
aSeg’s setSegmentStyle:(NSSegmentStyleTexturedRounded)
  
aSeg’s setFrame:(current application’s NSMakeRect(20, aHeight – 35, aWidth, aHeight – 40))
  
  
aSeg’s setTrackingMode:0
  
aSeg’s setTarget:me
  
aSeg’s setAction:"clickedSeg:"
  
aSeg’s setSelectedSegment:0
  
  
return aSeg
end makeSegmentedControlWithStartY

on clickedSeg:aSender
  set aSel to aSender’s selectedSegment()
end clickedSeg:

on getWidth(aDBounds)
  if class of aDBounds = list then
    –macOS 10.13 or later
    
return item 1 of item 1 of aDBounds
  else
    –macOS 10.10….10.12
    
return width of |size| of aDBounds
  end if
end getWidth

★Click Here to Open This Script 

Posted in GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAlert NSBox NSRunningApplication NSScrollView NSSegmentedControl NSView | 1 Comment

青空文庫のテキストのルビタグを超高速削除

Posted on 3月 18, 2019 by Takaaki Naganoya

青空文庫のテキストのルビタグをすべて削除するAppleScriptです。

CotEditorでオープン中の青空文庫のテキストからルビタグを削除し、元のドキュメントに置換結果を反映させます。

テストに使用したのは、夏目漱石の「こころ」のテキストです。上記ページの「テキストファイル(ルビあり)」をダウンロードして、Zipアーカイブを展開して使用しました。

ファイルサイズ373KB、当該部分4,570箇所。開始文字「《」、終了文字「》」で囲われたエリアをすべて削除するという、AppleScriptにはあからさまに不得意そうな処理で、最初に書いたAppleScriptでは1分半以上かかっていました。内容は、おおよそ常識的なサブルーチンを組み合わせてループで回しただけです。わざと遅くなるように組んだりはしていません。

これを、

 (1)CotEditorからの文章テキストの取得
 (2)置換当該箇所のリストアップ
 (3)文字置換
 (4)CotEditorへの文章テキストの転送

の4つのステージに分け、それぞれ処理時間を計測。すると、(1)、(2)、(4)については1秒かかるかかからないかぐらいの速度で実行していることが判明。圧倒的に(3)文字置換の処理に時間がかかっていました。

もともと文字置換には、AppleScript処理系最速のtext item delimitersを用いるサブルーチンを使用していました。これ以上、この方向に頑張っても速く処理することはできません。一応、ダメ元で4,570個の要素を持つ巨大なtext item delimitersを作成し一括処理できないか試してみたものの、さすがに処理系のキャパシティを超過しているようで処理が戻ってきません(迷走状態)。完全にお手上げです。

そこで、AppleScriptの処理系に依存したtext item delimitersによる処理をやめ、メモリ管理効率がよくないAppleScriptのstring型のデータで保持することをやめ、置換のたびにAppleScriptのstring型に変換(cast)することをやめ、置換中は最初から最後までNSMutableStringで管理するようにしました。

このように大幅に書き換えたところ、トータルで3.58秒で処理終了するようになりました。

すべての置換が終了したあとにNSMutableStringをAppleScriptのstringに変換し、CotEditorの最前面のドキュメントに結果を転送しています。

AppleScript名:青空文庫のテキストのルビタグを削除
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/18
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property NSScanner : a reference to current application’s NSScanner
property NSOrderedSet : a reference to current application’s NSOrderedSet
property NSMutableString : a reference to current application’s NSMutableString
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch

tell application "CotEditor"
  tell front document
    set aCon to contents
  end tell
end tell

set bCon to trimStrFromTo(aCon, "《", "》") of me

tell application "CotEditor"
  tell front document
    set contents to bCon
  end tell
end tell

–開始文字と終了文字に囲われた文字列をすべて削除する
on trimStrFromTo(aParamStr, fromStr, toStr)
  script hsAry
    property anArray : {}
    
property curStr : ""
  end script
  
  
set theScanner to NSScanner’s scannerWithString:aParamStr
  
set (anArray of hsAry) to {}
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {aResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
theScanner’s scanString:fromStr intoString:(missing value)
    
    
set {bResult, theValue} to theScanner’s scanUpToString:toStr intoString:(reference)
    
if theValue is missing value then set theValue to ""
    
    
theScanner’s scanString:toStr intoString:(missing value)
    
set the end of (anArray of hsAry) to (fromStr & theValue & toStr)
  end repeat
  
  
–Case: Not found
  
if length of (anArray of hsAry) = 0 then return aParamStr
  
  
–Uniquefy
  
set (anArray of hsAry) to makeUniqueListFrom((anArray of hsAry)) of me
  
  
–Replace strings as NSMutableString
  
set (curStr of hsAry) to NSMutableString’s stringWithString:aParamStr
  
repeat with i in (anArray of hsAry)
    set j to contents of i
    
set (curStr of hsAry) to ((curStr of hsAry)’s stringByReplacingOccurrencesOfString:(j) withString:"" options:(NSRegularExpressionSearch) range:{location:0, |length|:((curStr of hsAry)’s |length|())})
  end repeat
  
  
return (curStr of hsAry) as string
end trimStrFromTo

–1D Listをユニーク化(重複削除)
on makeUniqueListFrom(theList)
  set theSet to NSOrderedSet’s orderedSetWithArray:theList
  
return (theSet’s array()) as list
end makeUniqueListFrom

★Click Here to Open This Script 

Posted in list Text | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy CotEditor NSMutableString NSOrderedSet NSRegularExpressionSearch NSScanner | 2 Comments

アラートダイアログ上にBrowser+Map Viewを表示 v2

Posted on 3月 17, 2019 by Takaaki Naganoya

ダイアログ上に表示したNSBrowserで都道府県→都道府県別データを選択し、選択したデータの位置情報をMap Viewに表示するAppleScriptの改良版です。

初版掲載時のおかしな挙動を減らし、地図種別の切り替えができるようになっています。本掲載リストだけだと動作が完結しないため、ライブラリを含んだスクリプトバンドルをダウンロードして実行してください。下記リストは「参考までに」掲載しているものです。

–> Download whole Script bundle with Library

前バージョンでは、NSBrowser上でデータが存在していない箇所に空白のセルが表示され、クリックするとクラッシュするという状態でした。本バージョンでもたまに出てくるので完全ではないのですが、

 ・予想どおりデータの行数をカウントするハンドラで絞り込みを行うPredicates文の文字列に問題があった
 ・データに半角のシングルクォートが入っていて、これによってデータの絞り込みに問題が出た

という問題を解消しました。前者はこまめにstringにcastし、後者は全角文字に置き換えました。

それでもまだ問題が出るケースがあるので、まだしばらく実際に使いつつ様子見といったところでしょうか。

AppleScript名:アラートダイアログ上にBrowser+Map Viewを表示 v2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/10
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use framework "MapKit"
use scripting additions
use skLib : script "senjoNoKizunaLib"

property NSView : a reference to current application’s NSView
property NSAlert : a reference to current application’s NSAlert
property NSColor : a reference to current application’s NSColor
property NSBrowser : a reference to current application’s NSBrowser
property MKMapView : a reference to current application’s MKMapView
property NSScrollView : a reference to current application’s NSScrollView
property NSMutableArray : a reference to current application’s NSMutableArray
property MKMapTypeHybrid : a reference to current application’s MKMapTypeHybrid
property MKMapTypeSatellite : a reference to current application’s MKMapTypeSatellite
property MKMapTypeStandard : a reference to current application’s MKMapTypeStandard
property NSSegmentedControl : a reference to current application’s NSSegmentedControl
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn
property NSSegmentStyleTexturedRounded : a reference to current application’s NSSegmentStyleTexturedRounded

property zLevel : 17
property aMaxViewWidth : 1000
property aMaxViewHeight : 500
property theResult : 0
property returnCode : 0
property theDataSource : {}
property aSelection : {}
property aMapView : missing value
property aBrowser : missing value
property skDataList : {}

property prefList : {"北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県", "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県", "新潟県", "富山県", "石川県", "福井県", "山梨県", "長野県", "岐阜県", "静岡県", "愛知県", "三重県", "滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県", "鳥取県", "島根県", "岡山県", "広島県", "山口県", "徳島県", "香川県", "愛媛県", "高知県", "福岡県", "佐賀県", "長崎県", "熊本県", "大分県", "宮崎県", "鹿児島県", "沖縄県"}

if my skDataList = {} then
  set my skDataList to current application’s NSMutableArray’s arrayWithArray:(getSenjoNokizunaGameCenterDataList() of skLib)
end if

set tmpLen to length of (my skDataList as list)

set aSelection to {}

set paramObj to {myMessage:"Choose a Game Center", mySubMessage:("Choose an appropriate Game Center from list (" & tmpLen as string) & ") to play Senjo-no-Kizuna"}

my performSelectorOnMainThread:"chooseItemByBrowser:" withObject:(paramObj) waitUntilDone:true
if (my returnCode as number) = 1001 then error number -128

return my aSelection
–> {loc_id:"QIEXj9er5QSA_Y42-OjPNg", gcName:"THE 3RD PLANET ジャングルパーク鹿児島", latitude:31.5703088, longitude:130.5653137, address:"鹿児島県 鹿児島市 与次郎 1-11-1 フレスポジャングルパーク2F"}

on chooseItemByBrowser:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
  
— create a view
  
set theView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aMaxViewWidth, aMaxViewHeight))
  
set aMapView to MKMapView’s alloc()’s initWithFrame:(current application’s NSMakeRect(410, 30, aMaxViewWidth – 410, aMaxViewHeight – 30))
  
tell aMapView
    its setMapType:(MKMapTypeStandard)
    
its setZoomEnabled:true
    
its setScrollEnabled:true
    
its setPitchEnabled:true
    
its setRotateEnabled:true
    
its setShowsCompass:true
    
its setShowsZoomControls:true
    
its setShowsScale:true
    
its setShowsUserLocation:true
    
its setDelegate:me
  end tell
  
  
— make browser view with scroll view
  
set aScrollWithTable to makeBrowserView(prefList, 400, aMaxViewHeight) of me
  
  
–Segmented Controlをつくる
  
set segTitleList to {"Map", "Satellite", "Satellite + Map"}
  
set aSeg to makeSegmentedControl(segTitleList, 410, 0, 150, 20) of me
  
  
–Compose Views in NSView
  
theView’s setSubviews:{aScrollWithTable, aMapView, aSeg}
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
its setAccessoryView:theView
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
end chooseItemByBrowser:

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

on makeBrowserView(aList as list, aWidth as number, aHeight as number)
  set (my theDataSource) to NSMutableArray’s arrayWithArray:aList
  
  
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
set aBrowser to NSBrowser’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
  
aBrowser’s setDelegate:(me)
  
aBrowser’s setTarget:(me)
  
aBrowser’s setAction:"browserCellSelected:"
  
aBrowser’s setMinColumnWidth:120
  
aBrowser’s setSeparatesColumns:true
  
aBrowser’s setMaxVisibleColumns:2
  
aBrowser’s setAutohidesScroller:true
  
aBrowser’s setTakesTitleFromPreviousColumn:true
  
–aBrowser’s setBackgroundColor:(NSColor’s grayColor())
  
  
aScroll’s setDocumentView:aBrowser
  
aBrowser’s enclosingScrollView()’s setHasHorizontalScroller:true
  
aBrowser’s enclosingScrollView()’s setHasVerticalScroller:true
  
  
return aScroll
end makeBrowserView

–NSBrowser Event Handlers
on browser:aView numberOfRowsInColumn:aColumn
  if aColumn = 0 then
    return my theDataSource’s |count|()
  else if aColumn = 1 then
    set aPath to (text 2 thru -1 of ((aView’s |path|()) as string)) as string –ここが問題だったもよう
    
set tmpArray to (my filterRecListByLabel1(skDataList, "address BEGINSWITH ’" & aPath & "’")) as list
    
return (length of tmpArray)
  else
    return 0
  end if
end browser:numberOfRowsInColumn:

on browser:aView willDisplayCell:(aCell) atRow:(rowIndex as integer) column:(colIndex as integer)
  if colIndex = 0 then
    –Prefectures
    
aCell’s setTitle:((item (rowIndex + 1) of prefList) as string)
    
aCell’s setLeaf:false
    
  else if colIndex = 1 then
    –Each Game Centers in the Prefecture
    
set aPath to text 2 thru -1 of ((aView’s |path|()) as string)
    
set tmpArray to my filterRecListByLabel1(skDataList, "address BEGINSWITH ’" & aPath & "’")
    
set tmpItem to (tmpArray’s objectAtIndex:rowIndex)
    
    
set aGameCenterName to (tmpItem’s gcName) as string
    
aCell’s setTitle:(aGameCenterName)
    
aCell’s setLeaf:true
    
  else if colIndex ≥ 2 then
    error "Wrong NSBrowser status"
  end if
end browser:willDisplayCell:atRow:column:

on browserCellSelected:aSender
  set aPath to my aBrowser’s |path|()
  
set aList to (aPath’s pathComponents()) as list
  
set aLen to length of aList
  
  
if aLen = 3 then
    –set aPref to contents of item 2 of aList
    
set aGc to contents of last item of aList
    
    
set tmpArray to my filterRecListByLabel1(skDataList, "gcName == ’" & aGc & "’")
    
–set tmpArray to my filterRecListByLabel1(skDataList, "gcName == " & aGc)
    
set tmpItem to contents of first item of (tmpArray as list)
    
    
copy tmpItem to my aSelection
    
    
set aLatitude to (latitude of tmpItem) as real
    
set aLongitude to (longitude of tmpItem) as real
    
    
tell aMapView
      set aLocation to current application’s CLLocationCoordinate2DMake(aLatitude, aLongitude)
      
its setCenterCoordinate:aLocation zoomLevel:(zLevel) animated:false
    end tell
    
  end if
end browserCellSelected:

–NSArrayに入れたNSDictionaryを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList, aPredicate as string)
  set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aRecList’s filteredArrayUsingPredicate:aPredicate
  
return filteredArray
end filterRecListByLabel1

–Segmented Controlをつくる
on makeSegmentedControl(titleList, startX, startY, aWidth, aHeight)
  set aLen to length of titleList
  
  
set aSeg to NSSegmentedControl’s alloc()’s init()
  
aSeg’s setSegmentCount:aLen
  
  
set aCount to 0
  
repeat with i in titleList
    set j to contents of i
    (
aSeg’s setLabel:j forSegment:aCount)
    
set aCount to aCount + 1
  end repeat
  
  
aSeg’s setTranslatesAutoresizingMaskIntoConstraints:false
  
aSeg’s setSegmentStyle:(NSSegmentStyleTexturedRounded)
  
aSeg’s setFrame:(current application’s NSMakeRect(startX, startY, aWidth, aHeight))
  
aSeg’s setTrackingMode:0
  
aSeg’s setTarget:me
  
aSeg’s setAction:"clickedSeg:"
  
aSeg’s setSelectedSegment:0
  
  
return aSeg
end makeSegmentedControl

–Segmented Controlのクリック時のイベントハンドラ
on clickedSeg:aSender
  set aSel to aSender’s selectedSegment()
  
set tList to {MKMapTypeStandard, MKMapTypeSatellite, MKMapTypeHybrid}
  
set tmpType to contents of item (aSel + 1) of tList
  
  
aMapView’s setMapType:(tmpType)
  
  
set selSeg to aSel
end clickedSeg:

★Click Here to Open This Script 

Posted in geolocation GUI list Map regexp | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy MKMapTypeHybrid MKMapTypeSatellite MKMapTypeStandard MKMapView NSAlert NSAlertSecondButtonReturn NSBrowser NSColor NSMutableArray NSRunningApplication NSScrollView NSSegmentedControl NSSegmentStyleTexturedRounded NSView | Leave a comment

Finderの環境設定によって得られる書類ファイル名が変わるアプリケーション

Posted on 3月 17, 2019 by Takaaki Naganoya

Apple純正のKeynote、Numbers、Pagesの動作で注意を要する動作が見られました。

macOSの各種設定については、デフォルト設定状態をなるべく変更しないで使っています(変更するのはマウス移動速度とキーボードリピート速度ぐらい?)。

Finderの環境設定についても、初期設定値からたいして変更していません。

このFinderの環境設定の中に、「すべてのファイル名拡張子を表示」という項目があります。設定するかどうかは個人の趣味によるところですが、自分はオンにしていません。デフォルトではオフなので、デフォルト値のまま使うことが多いところです。

でもまさか、オンにしておくと挙動が変わるアプリケーションが出てくるとは思ってもみませんでした。そういうのはFinder上だけの挙動だと思ってしまうところです。

AppleScript経由で得られるiWorkアプリケーションのファイル名に変化が

ご覧のとおり、Finderの設定によってiWorkアプリケーションに対してname of documentを取得したときに得られる名前が違うということを確認できました。Finderの環境設定を変更するたびにiWorkアプリケーションはいったん終了させています(起動しっぱなしだとFinderの環境設定値の変更を認識しないようなので)。

tell application "Numbers"
  set aName to name of front document
end tell

★Click Here to Open This Script 


▲Finder上の設定=「すべてのファイル名拡張子を表示」をオフ → ”name_test”


▲Finder上の設定=「すべてのファイル名拡張子を表示」をオン → ”name_test.numbers”

macOS 10.12.6、macOS 10.13.6、macOS 10.14.4Betaで挙動を確認しましたが、すべて同様の動作が確認されました。

これは、、、、、自分はよくない動作だと思いますが、まずはFinderの環境設定値「すべてのファイル名拡張子を表示」の設定内容をScriptの実行環境で統一してどちらかに設定しておく必要性を感じます。

Posted in System | Tagged 10.12savvy 10.13savvy 10.14savvy Keynote Numbers Pages | Leave a comment

HTMLをplain textに変換(文字コード自動認識ライブラリ展開)

Posted on 3月 14, 2019 by Takaaki Naganoya

HTMLをプレーンテキストに変換するAppleScriptです。

本来は、文字コードの自動推測を行う部分はライブラリ化していますが、掲載用にライブラリをScript中に展開してみました。

コードの自動推測部分の処理は、昔のCotEditorのソースを読んで前半部分の「古くからある文字エンコーディングの勝ち抜け」処理を書き、後半部分のマイナー文字エンコーディングの多数決&文字化け検出方式がオリジナル処理部分です。

本コード自動推測は、意味のある日本語のテキストを処理するように設計してあるので、寿司屋の湯呑みのように魚の名前の漢字が1文字で羅列されているようなテキストの文字コード自動判別でミスを起こす可能性があります(実際に魚の名前のテキストを作って読み込んでみましたが、とくに問題はありませんでした)。

ただ、最近はUTF-8だけでなんとかなりそうなので、そこまでエグいテキストに遭遇することもないでしょう。

AppleScript名:HTMLをplain textに変換(文字コード自動認識ライブラリ展開)
— Created 2017-09-08 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–use jLib : script "japaneseTextEncodingDetector"

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

set aFile to choose file
set aRes to readJapanesTextFileWithGuessingEncoding(POSIX path of aFile) of me
if aRes = false then return ""

set aPlainText to HTMLDecode(aRes) of me

on HTMLDecode(HTMLString)
  set theString to current application’s 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

–Read Japanese text with detecting its text encoding
on readJapanesTextFileWithGuessingEncoding(aPOSIXpath as string)
  
  
–ISO2022JP check
  
set aNSData to current application’s NSData’s dataWithContentsOfFile:aPOSIXpath
  
set aDataLength to aNSData’s |length|()
  
if aDataLength > 1024 then set aDataLength to 1024
  
  
–0x1B check
  
set anNSString to current application’s NSString’s stringWithString:(character id 27) — 0x1B
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set theRange to aNSData’s rangeOfData:theData options:0 range:(current application’s NSMakeRange(0, aDataLength))
  
  
–found 0x1B in aNSData
  
if |length| of theRange = 1 and location of theRange < aDataLength then
    set aStr to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:(current application’s NSISO2022JPStringEncoding)) –21
    
if aStr is not equal to missing value then return (aStr as text) — ISO2022JP
  end if
  
  
–EUC
  
set resValue to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:(current application’s NSJapaneseEUCStringEncoding))
  
if resValue is not equal to missing value then return (resValue as text)
  
  
–UTF-8
  
set resValue to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:(current application’s NSUTF8StringEncoding))
  
if resValue is not equal to missing value then return (resValue as text)
  
  
–SHift JIS
  
set resValue to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:(current application’s NSShiftJISStringEncoding))
  
if resValue is not equal to missing value then return (resValue as text)
  
  
–多数決を取る
  
–UTF-16BE/LE/無印Unicodeは多数決を取る
  
set resValue1 to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:(current application’s NSUTF16BigEndianStringEncoding)) as text
  
set sample1 to getTextSample(resValue1) of me
  
set lang1 to specifyLanguageOfText(sample1) of me
  
set para1 to length of (paragraphs of sample1)
  
set words1 to length of (words of sample1)
  
  
–UTF-16LE  
  
set resValue2 to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:(current application’s NSUTF16LittleEndianStringEncoding)) as text
  
set sample2 to getTextSample(resValue2) of me
  
set lang2 to specifyLanguageOfText(sample2) of me
  
set para2 to length of (paragraphs of sample2)
  
set words2 to length of (words of sample2)
  
  
–無印Unicode
  
set resValue3 to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:(current application’s NSUnicodeStringEncoding)) as text
  
set sample3 to getTextSample(resValue3) of me
  
set lang3 to specifyLanguageOfText(sample3) of me
  
set para3 to length of (paragraphs of sample3)
  
set words3 to length of (words of sample3)
  
  
–文字および文法的に見て「日本語」ならそれを返す
  
if lang1 = "ja" then return resValue1
  
if lang2 = "ja" then return resValue2
  
if lang3 = "ja" then return resValue2
  
  
  
–文字化けしたときには、日本語の「Word」として認識されづらく、Paragraphも少ない(1とか)なので条件で除外する
  
if para1 is not equal to 1 then
    if (words1 ≤ words2) or (words1 ≤ words3) then
      return resValue1
    end if
  end if
  
  
if para2 is not equal to 1 then
    if (words2 ≤ words1) or (words2 ≤ words3) then
      return resValue2
    end if
  end if
  
  
if para3 is not equal to 1 then
    if (words3 ≤ words1) or (words3 ≤ words2) then
      return resValue3
    end if
  end if
  
  
return false –文字コード判定に失敗した
end readJapanesTextFileWithGuessingEncoding

on specifyLanguageOfText(aStr)
  set aNSstring to current application’s NSString’s stringWithString:aStr
  
set tagSchemes to current application’s NSArray’s arrayWithObjects:(current application’s NSLinguisticTagSchemeLanguage)
  
set tagger to current application’s NSLinguisticTagger’s alloc()’s initWithTagSchemes:tagSchemes options:0
  
tagger’s setString:aNSstring
  
set aLanguage to tagger’s tagAtIndex:0 |scheme|:(current application’s NSLinguisticTagSchemeLanguage) tokenRange:(missing value) sentenceRange:(missing value)
  
return aLanguage as text
end specifyLanguageOfText

on getTextSample(aText)
  set aLen to length of aText
  
if aLen < 1024 then
    set bLen to aLen
  else
    set bLen to 1024
  end if
  
return (text 1 thru bLen of aText)
end getTextSample

★Click Here to Open This Script 

Posted in file Text | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAttributedString NSMutableArray NSString | Leave a comment

Post navigation

  • Older posts
  • Newer posts

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

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • macOS 15, Sequoia
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Numbersで選択範囲のセルの前後の空白を削除
  • メキシカンハットの描画
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • AppleScriptによる並列処理
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • macOS 15でも変化したText to Speech環境
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • デフォルトインストールされたフォント名を取得するAppleScript
  • 【続報】macOS 15.5で特定ファイル名パターンのfileをaliasにcastすると100%クラッシュするバグ
  • Script Debuggerの開発と販売が2025年に終了
  • macOS 15 リモートApple Eventsにバグ?
  • AppleScript入門① AppleScriptってなんだろう?
  • macOS 14で変更になったOSバージョン取得APIの返り値
  • NSObjectのクラス名を取得 v2.1
  • macOS 15:スクリプトエディタのAppleScript用語辞書を確認できない
  • 有害ではなくなっていたSpaces
  • Xcode上のAppleScriptObjCのプログラムから、Xcodeのログ欄へのメッセージ出力を実行

Tags

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

カテゴリー

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

アーカイブ

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

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

メタ情報

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

Forum Posts

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

メタ情報

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