iWork Apps(Keynote、Pages、Numbers)がバージョン14.2にアップデートしました。
すべてのアプリでAppleScript用語辞書への変更はありません。
Keynote 14.2では、macOS 15, Sequoia上でHDRコンテンツの表示をサポートしています。
バグの修正がうたわれていますが、Pagesのページ上のオブジェクトの情報を取得できる範囲が現在表示中の見開き+6ページに制限されるというアホな仕様は変わっていないことを確認しています。
iWork Apps(Keynote、Pages、Numbers)がバージョン14.2にアップデートしました。
すべてのアプリでAppleScript用語辞書への変更はありません。
Keynote 14.2では、macOS 15, Sequoia上でHDRコンテンツの表示をサポートしています。
バグの修正がうたわれていますが、Pagesのページ上のオブジェクトの情報を取得できる範囲が現在表示中の見開き+6ページに制限されるというアホな仕様は変わっていないことを確認しています。
オープンソースのプロジェクト「iCal4ObjC」をFramework化してAppleScriptから呼び出し、iCalendarファイル(.ics)をデスクトップに作成するテストコードです。実行には、iCalendarKit.framework(macOS 10.15以降用にUniversal Binaryでビルド)を必要とします。また、macOS標準搭載のスクリプトエディタ上では動作せず、Script DebuggerないしSDから書き出したEnhanced Appletとして動かす必要があります。
–> DownloadiCalendarKit.framework (To ~/Library/Frameworks)
本Scriptの実行結果です。
AppleScript名:iCal4ObjCのじっけん(iCalendarファイルの作成).scptd |
— – Created by: Takaaki Naganoya – Created on: 2024/09/20 — – Copyright © 2024 Piyomaru Software, All Rights Reserved — use AppleScript use framework "Foundation" use framework "iCalendarKit" –https://github.com/cybergarage/iCal4ObjC use scripting additions set ical to current application’s CGICalendar’s alloc()’s init() –Add an object set icalObj to current application’s CGICalendarObject’s alloc()’s initWithProdid:"//CyberGarage//iCal4ObjC//EN" –Add a component set icalComp to current application’s CGICalendarComponent’s alloc()’s initWithType:"VTODO" icalObj’s addComponent:icalComp ical’s addObject:icalObj — Add a property set icalProp to current application’s CGICalendarProperty’s alloc()’s init() icalProp’s setName:"SUMMARY" icalProp’s setValue:"Write report" icalComp’s addComponent:icalProp set outPath to POSIX path of (path to desktop) & (do shell script "uuidgen") & ".ics" ical’s writeToFile:outPath |
Script DebuggerがmacOS 15上で起動しない状況が続いています。
日本語ユーザー環境、英語ユーザー環境にかかわらずmacOS 15/macOS 15.1上でScript Debuggerが起動しません。
ちなみに、macOS 14.7ではユーザーディレクトリ下のFrameworkを実行できない問題も発生しており、macOS 14/15を信用せずにメイン環境をmacOS 13.7のままにとどめていますが、この判断が大正解のようです。
続報:
rm ~/Library/Preferences/.2xbG4@@Ght01%!020#u
rm ~/Library/Preferences/.2xbZ4@@Ght01%!010#u
rm ~/Library/Application\ Support/.1@xX4D@yyt02"!2&0#a
rm ~/Library/Application\ Support/.1@xX4D@yytT2"!2&1#a
とすることで、macOS 15.x上でもScript Debuggerが起動できました。実際には、Terminal上でこれをそのまま実行しても削除できなかったので、当該フォルダに移動してTerminal上でファイルを確認したうえで削除しました。
SDが起動できないと、Framework呼び出しを行なっているAppleScriptの実行や書き出しに支障が出るため、実に困ります。
ただ、SDのアップデートやメンテナンスが数少ない開発者によって支えられている以上、代替策も用意しておきたいところです。
FinderやSystem Eventsで扱っているファイルの種別(kind)属性情報が、macOS 14以降で変更になっていて困っている件、その後の続報です。
おそらく、多言語ローカライズを行なっている下請け業者(いろんなメーカーのローカライズを担当している多言語ローカライザー企業)へのApple側からの指示内容に何か変化があって、不必要な部分までローカライズ内容の統一(≒修正)が行われてしまったのだろう、と見立てていました。
実際に身の回りのマシンで調査したところ、おおよそその見立てで間違っていないようです。
もとになっている英語版の表記については割と一貫性があって、コロコロ方針が変わっているようには見えません。
しかし、日本語ローカライズされた属性値については不可解というか「これを指示した人間の知性を疑う」という変わりっぷりであり、そこまで想定していなかったUS Apple側の指示内容がいいかげんだった、という想像を裏付けるものとなっています。
ローカライズにおいては変更しても意味がないし、変更することで悪影響が出る部分もあるわけですが、そこまで細かくチェックしていないということなんでしょう。
# 訂正:Script Debugger自体を再起動したら動くようになりました。何をやってもmissing valueしか返らなくなってビビりました。
ユーザーディレクトリ下のCocoa FrameworkをロードしてAppleScriptから実行可能なScript DebuggerおよびそのEnhancesd Applet。最新版のv8.0.8で、ホームディレクトリ下に配置したこれらCocoa Frameworkの実行ができないことを確認しています。
ちょっと凝ったAppleScriptでバンドル内にFrameworkを入れて呼び出しているものについては、目下実行できない状況にあります。自分が確認したのはmacOS 14.7環境。呼び出したはいいものの結果にmissing valueが返ってきて、首をひねっていたところ、原因がScript Debuggerにあることを確認。
自分でビルドした野良Cocoa Frameworkや、Bridge Plusの内蔵Frameworkまで、みんな呼び出せない状況です(日本語ユーザー環境)。
画像の余白トリミングから2D Arrayの高速ソーティングなど、Framework呼び出しは日常的なAppleScriptの実行に欠かせないものです。
Appleは何の発表もなく、重要な情報を変更することがあります。とくに、日本語ローカライズされた内容だと英語圏のユーザーの関心は薄いですし、Appleのエンジニアが「理解できない」ケースも見られます。日本語の単語分解(≒形態素解析)について、macOS 10.5あたりまで「文字種類が変わったら単語の切れ目」とかいう無茶苦茶なロジックで処理されていましたが、それを「そういうのじゃない」とことあるごとにレポートしてきましたが、理解されてきませんでした。
Finderが管理しているファイルの種別(kind属性)は、ローカライズされたものが返ってくることで知られていましたが、macOS 14でそれが変更されたことがわかりました(最近MacBook Air M2を追加して、macOS 14環境を真剣に使い始めたので)。
こんな重要な変更は、気軽に行なってほしくないですし、変更したことを通達してほしいのですが(ーー;
以前からFinderのkindの内容については「ローカライズしてほしくないなぁ」と感じていましたが、このあたりのファイル種別については、UTI(com.image.pngなど)を利用するほうが、利用者の言語環境の変化を意識しなくていいですし、こうした些細なローカライズの変更の影響を受けなくてよいでしょう。
ただ、このあたりのやり方がいつも乱暴です>Apple
Release Notesもまともに書かなくなりましたし、どういうつもりなんだか。
Appleへの指摘事項として、Shortcutsで画像を扱うアクションが「写真」だったり「イメージ」だったり「画像」だったりと、用語のゆらぎが激しいという話があります。この点はAutomatorの登場時を彷彿とさせるものがあります。「これで使えというのには無理がある」「類似語の表記ゆらぎを吸収する仕組みが必要なのでは?」という指摘を行なっているのですが、そういう方向の努力として「種別」(Kind)のローカライズに手を入れた……と、考えられなくはありません。
それにしても、やっていることがお粗末すぎます。
AppleScript名:kind checker |
tell application "Finder" set aSel to selection set anItem to first item of aSel properties of anItem end tell |
iWork Apps(Keynote、Pages、Numbers)にはAppleScript系の機能でいろいろ勘弁してほしい仕様がありますが、その中でも最大のものが、書類内の各オブジェクトがIndexで管理されていることです。
Indexというのは、1からはじまる連番の数値です。
iWork Apps(Keynote、Pages、Numbers)のドキュメント上でオブジェクトの削除を行おうとすると、「地獄」を見ることになります。
iWork Appsの書類上でオブジェクトの削除を行うと、このIndex値が振り直されてしまうために、正しく「対象」を指し示すことが(途中から)できなくなってしまうのです。あるいは、まったく関係のない別のオブジェクトが削除されるとか。
これが、idで管理されていれば、何かのオブジェクトを削除されても影響はありません。何かUUID的な重複しないidが割り振られて、最初から最後まで(アプリケーションが起動してから終了するまで、あるいは書類がオープンされてからクローズされるまで)個別に識別されます。
各オブジェクトを識別する場合に、それ専用の属性値を持たせることがあります。
Adobe InDesign:script label AppleScriptから設定・確認が可能なラベル(文字列)を設定できる
もともとある仕組みを本来の目的以外の用途に使うことができるケースもあります。
OmniGraffle:URL urlとかいいつつ、文字列だったらだいたいなんでも入った記憶が
そして、iWork Apps(Keynote、Pages、Numbers)。普段ほとんど使わなくて、AppleScriptから操作できて害のない属性値なんて便利なものがあるわけが……ありました。
Keynoteで使っている例は見かけましたが、他で使ったことのない「reflectin value」。つまり、「反射」属性。
PagesでもNumbersでも使ったことがありません。個人的には、Keynoteでも使ったことがないと思います。
選択状態にあるオブジェクトをもとに何らかの処理を行なって、選択していたオブジェクトを削除する場合に、普通に処理すると(オブジェクトがIndexで管理されているので)、地獄を見ます。
なので、削除対象のオブジェクトのreflection valueに100とか(1〜100のうち、0でないお好きな値を)を設定しておいて、ひととおり処理し終わったら、reflection valueが指定値になっているオブジェクトだけをフィルタ参照で削除する、といった対策が有効です。
前処理Scriptで、最初にreflection valueが何か設定されているオブジェクトが存在しないことを確認したうえで、選択中のオブジェクトのreflection valueに100を設定し……
AppleScript名:削除_前処理.scptd |
tell application "Pages" tell front document set reList to every iWork item whose reflection value is 100 if length of reList is not equal to 0 then return set aSel to selection repeat with i in aSel set j to contents of i set reflection value of j to 100 end repeat end tell end tell |
処理を行なったあとで、reflection valueに100と設定されているオブジェクトを削除します。
AppleScript名:削除_後処理.scptd |
tell application "Pages" tell front document tell current page set aSel to delete (every iWork item whose reflection value = 100) end tell end tell end tell |
Pixelmator Proがv3.6.8でHDR画像のサポートを追加しました。
AppleScript用語辞書の比較で確認された変化は以下のとおりです。
・予約語「JPEG」の類義語として「JPG」を追加定義
・HDR画像の予約語を追加(OpenExr、HDR JPEG、HDR HEIC、HDR AVIF、HDR PNG)
・applicationのプロパティとして「load hdr content」を新設
・documentのプロパティとして「display hdr content」を新設
Appleの公式資料によれば、現行機種はHDRに対応しているとのこと。
ただし、接続しているディスプレイによって、
「このディスプレイはHDRコンテンツをサポートしていません」(Mac mini M1+Apple Cinema Display)
「このディスプレイはHDRコンテンツのサポートが制限されています」(MacBook Air M2)
といったように、非対応/対応(不完全対応を含む)と状態が分かれるようです。
対応環境でPixelmator ProのHDRサポート状態を変更してHDR画像をオープンする実験は行なってみました。
▲Pixelmator Proの設定ウィンドウ HDRコンテンツのサポートが制限されている(MacBook Air M2)と表示
▲Pixelmator ProでサンプルのHDR JPEGをオープンしたところ
AppleScript名:HDR画像をオープン |
tell application "Pixelmator Pro" set load hdr content to true set aHDR2 to load hdr content –> true if aHDR2 = false then return set aHDRfile to choose file open aHDRfile end tell |
HDR画像をオープンしても、HDR表示が行われるかどうかは別の管理になっている(documentのプロパティ)ので、そこも設定する必要があるようです。変更すると画面上でわずかに階調表現に変化が見られました(ちょっとだけ)。
AppleScript名:オープン中のHDR画像をHDR表示する |
tell application "Pixelmator Pro" –環境チェック set appHDR to load hdr content if appHDR = false then return –HDR画像の表示状態を変更 tell front document set display hdr content to true set docHDR to display hdr content end tell end tell |
電子書籍新刊「AppleScriptでたのしむ レトロ・グラフィック プログラム集」を刊行しました。全154ページ、サンプルAppleScriptアーカイブつき。
→ 販売ページ
1980年代や90年代の8/16ビットPCのBASICで描かせていた、三角関数による各種グラフィックスをAppleScriptで再現。ダイアログで表示するだけでなく、各種GUIアプリ(Keynote、Numbers、Pages、Excel、PowerPoint、Word、Pixelmator Pro)を操作して描画したり、画像書き出ししてAirDropでiOSデバイスに転送するようなサンプルを収録しています。
懐かしのレトロCGの世界を再現
時代を経て感じる郷愁とも異なるテイスト
その昔、十数分かけて描いた三角関数グラフ
1秒以下で終了 vs 6分で終了
最新環境で動くAppleScriptにBASICのプログラムを移植
アップルスクリプトは、構文色分け必須の、色で要素を見分ける環境
最低限の知識でAppleScriptによるグラフィックを
AppleScript書類内に、実行に必要なライブラリを同梱
筆者の関数計算ライブラリ「calcLibAS」内蔵関数
コラム ポケコンエミュレータ“pockemul”
1994年から採用され続けている言語
GUIアプリを操作するために存在。搭載実行環境がとても多い
書き方は、アプリ内に存在する用語辞書を参照
本来の機能を利用するためにはシステム設定で許可する必要が
10.10以降でCocoaを直接呼べるようになったインタプリタ言語
GUI部品を直接操作してアプリを操作する強制操作機能が人気?
Web上のAPIを呼んでクラウド系の機能も利用
AS自体で予約語と機能を記述するライブラリ機能
コラム AppleScriptの世界の全体像 OS機能の最深部からGUIそのものの操作まで
Cocoaの機能を呼び出してメモリ上で画像を作成
NSAlertの上にNSImageViewを作成しグラフィック表示
Cocoaのグラフィックス座標系”
主要なアプリケーションの座標系”
画像ファイルに書き出せば”
他のアプリにコピー&ペースト”
当時は存在していなかった透過画像”
パラメータを変えると動作が変わる”
コラム GUIアプリごとの応答速度の違い
スクリプトエディタでオープンして実行するだけ
必要なライブラリはバンドル内にすべて格納
掲載リストはグラフィックス描画にかかわる箇所のみ
How to use/ダイアログ表示AppleScript
How to use/ファイル出力AppleScript
How to use/ファイル出力+AirDrop AppleScript
How to use/クリップボード転送AppleScript
How to use/各種GUIアプリ操作AppleScript
OS標準搭載の13の実行環境およびサードパーティの数十の実行環境
線画テスト
円画テスト①
円画テスト②
サイクロイド曲線
バラ曲線
パスカルの蝸牛形
リサージュ曲線
ダイヤモンドパターン
アルキメデスの螺旋
メキシカンハット①
メキシカンハット②
メキシカンハット③
メキシカンハット④
コラム マシンごとの実行速度の違い
Windowsの開発者と話をしていて、「複数のGUIアプリを操作して自動化するという話が理解できない」と言われます。
ファイル情報(選択中のファイル、選択中のフォルダ)をFinderから取得して
spotlight機能を用いて選択中のフォルダ以下のPages書類を抽出し
個別にPagesでオープンしてPDFを書き出し
書き出したPDFをファイル名順にならべかえて連結し
DropboxにREST API経由でアップロードして共有し
得られた共有URLを指定の送付先にメール(Mail.app)で送信する
と、複数のGUIアプリを操作した自動化のワークフローは、普通にありえるものです。ありふれすぎています。
ここで、ひとことに「自動化」といったときに、ユーザーごとに想定する「サイズ」がぜんぜん違うことに気づきます。
・(A)メニュー項目をいくつか連続して操作する「操作自動化」
・(B)1つの連続した操作を繰り返して実行する「作業自動化」
・(C)MacがタイマーでいくつかのScriptを実行してロボット動作する「業務自動化」
(A)<(B)<(c)と粒度の大きさが変わるところで、世間で言われる「自動化」というのは、ほぼ(A)の操作自動化のレベルと思われます。
自分が行なっているのは、(B)や(c)のレベルの自動化です。
(A)のレベルの「操作自動化」が悪いとは言いませんし、そういうレベルでも自動化に取り組むことには意義はあることでしょう。
ただ、(B)(c)と比べると成果として得られる「メリットが少ない」とは思います。操作を行うための心理的な障壁を下げる、という意味ではメリットがあるのかもしれませんが……。
(B)や(C)では、まとまった動作を放置状態のMacで行うものであり、人間が作業を眺めている必要はないものです。
(B「作業自動化」)や(C)「業務自動化」では、人間がいちいち指示する必要がないか、Scriptの動作を開始する場合にのみ対象フォルダなどのパラメータを指定するものです。
独立した作業なり業務を実行できるようにしておけば、実行するMacの台数を増やせば、それだけ同時にこなせる仕事が増やせます。
それだけ自動化してあれば、作業のためにアルバイトを雇う必要もありませんし、夜通し作業を行わせておいてもよいでしょう。
これらのまとまった作業を行うAppleScriptを作って運用する場合には、開発前に「仕様書」が必要になります。たいしたものでなくてもいいですが、どういった作業をどういう手順で行うのか、プログラムに書く前に人間の言葉でまとめておく必要はあるのです。
それは、箇条書きの文章で書いておいてもいいですし、画面キャプチャをまとめておいてもよいでしょう。
仕様書を書かないと、どこに向かって、どのように進んでいくのかを誰も理解していないという話になってしまいますし、それではScriptはおろか関与する人々が誰も「正しい作業」を理解できていないという話になってしまいます。
macOSのバージョンを求める機能については、かならず複数の方法で取得できるように調査しています。
それは、Appleがバグを作る可能性が一番高い機能だからです(なんで懲りないんだろ?)。
OSバージョンを求めるのに、
・AppleScriptの標準装備コマンド「system info」で求める
–> {AppleScript version:”2.8″, AppleScript Studio version:”1.5.3″, system version:”13.6.9″, short user name:”XXXX”, long user name:”XXXXX XXXXX”, user ID:504, user locale:”ja_JP”, home directory:alias “Macintosh HD:Users:XXXX:”, boot volume:”Macintosh HD:”, computer name:”M1 mini”, host name:”m1mini.local”, IPv4 address:”192.168.0.99″, primary Ethernet address:”99:99:99:99:ZZ:99″, CPU type:”ARM64E”, CPU speed:missing value, physical memory:16384}
・AppleScriptの環境プロパティ「system attribute」を求める
set v1 to system attribute “sys1” –> 10
set v2 to system attribute “sys2” –> 4
set v3 to system attribute “sys3” –> 11
・shell commandで求める
–> sw_vers
–> sw_vers -productVersion
などに加えて、NSProcessInfos processInfo()’s operatingSystemVersion() を呼び出すという方法も加わりました。
ただ、これが最近おかしな挙動をするようになったのに気づいていろいろ調査してみたら、macOS 14になって返り値のフォーマットを変えたことが判明しました。ラベルつきの値(NSDictionary)で返してきたのが、ラベルなしの配列(NSArray)で返すように変わっています。macOS 10.13で座標系のデータをラベルなしで返すように変更してきたのと同様の変更が加えられています。
macOS 14.xの初期バージョンでは従来タイプの値を返してきたように記憶していますが、いつ変更したのやら。
AppleScript名:OSバージョンを求める.scpt |
use AppleScript use framework "Foundation" use scripting additions set pInfo to (current application’s NSProcessInfo’s processInfo()’s operatingSystemVersion()) –> {majorVersion:10, minorVersion:14, patchVersion:6}–10.14 –> {majorVersion:13, minorVersion:6, patchVersion:9}–13.6.9 –> {14, 6, 1}–14.6.1 –> {15, 1, 0}–15.0.1 |
こういう基本的な情報を提供するAPIの仕様を変更することに、どういうインセンティブがあるのか不明です。変える価値があると判断したから変えたんでしょう。自分にはどういう理由なのか、さっぱり分かりません。
安全のためには、AppleScriptのビルトイン機能を使うか、OS内のplistを読み取って自分で処理するとかいった「対策」が必要なのでしょう。
AppleScriptを使ったことのない人が、AppleScriptを使うときに、どのような考え方で問題を解決したらよいでしょうか?
AppleScriptの一番やりやすい作業は、アプリケーションの書類を大量に作成/チェックするというものです。
書類をともなうアプリで、AppleScriptに対応しているものは、AppleScriptによる書類作成やチェックの自動化が行える可能性が高いと考えてよいでしょう。
Word、Excel、PowerPoint、Pages、Numbers、Keynoteなどのオフィスアプリ。
InDesign、Illustrator、Photoshopといったクリエイティブ系のアプリ。
そうしたアプリを中心に作業を自動化したい、という話はとても「やりようがある」と考えます。難易度が低いわけではありませんが、何か困っていて解決したいことがある場合には、解決できる可能性が高い、ということです。
日常的に使う機会の多いWebブラウザやメーラーで行う作業をまとめて自動化することができます。
やや、難易度が高くなりますが……Webブラウザを操作して特定のWebサイトにログインして、データを取得し、ログアウトするという操作は実際にAppleScriptで行えるものの代表的なものです(Webサイト側の「作り方」によってうまく操作できないこともあります)。
メーラー関係の作業についても、大量のメールを、内容を変更しつつ送信するという作業はAppleScriptに適した作業です。ただし、日常的に利用しているメーラーが、Appleのメール.appである必要があります。他のメーラーではAppleScriptへの対応度が低いとかまったく対応していないケースがあるため、これ以外の選択肢がないといって差し支えないでしょう。
AppleScriptに特殊なデータを扱うような(他のGUIアプリのデータを、アプリなしで読み書きして操作するような)機能は用意されていません。
テキスト、画像、PDF、QuickTimeムービーといった一般的なデータであれば、AppleScriptだけで処理できる可能性は高いところです。とくに、PDFについてはmacOS 10.10以降でmacOSのAPI CocoaをAppleScriptから直接呼べるようになったために、ウォーターマークを追加する「以外」の操作であれば実際に処理できます。
当Blogでは、日々AppleScriptを書いている中で、「この情報は有用なので共有しておくべき」と考えたものを掲載し、すぐに使っていただけるようにして公開しています。
書き方、使い方、詳しい説明については、電子書籍にまとめて約90冊ほど出しています。
つまり、当BlogはAppleScriptが何であるかを理解していて、実際に書ける人に対して情報を発信する立場です。
ただ、ちょうど夏休みということもあり、ちょっとそのあたりについて書いてみることにしました。
「macOS上で動くマクロ言語」とか言うと、何か理解できたような気になりますが……これだけでは何も説明わかりません。
macOS上の機能、各種GUIアプリの機能、shell scriptの機能など、さまざまな機能を呼び出して、データをやりとりする、インタプリタ型(逐次解釈実行型)のプログラミング言語です。
macOS上の大きな機能や小さな機能を呼び出せるようになっていて、とくにGUIアプリの操作や、GUIアプリの設定情報や、GUIアプリがオープンしている「書類」にアクセスして情報をとってきたり、設定したり……という操作が行えます。
AppleScriptがGUIアプリを操作する方法は、いくつか手段が用意されています。
AppleScriptがGUIアプリを操作するやりかたは、マウスカーソルを操作してメニューを選ぶといった方法ではありません(別途ありますが)。それでは、遅すぎるし、画面に表示されていなかったら操作できないですし、「できないこと」が多すぎます。
なので、あらかじめ「こういうコマンドを受け付けるよ!」というコマンドの一覧がAppleScriptに対して読める形でアプリ内に格納されています。ユーザーもこのコマンド一覧(用語辞書)を読んで、AppleScriptを書くことになります。
「メニューのx番目の項目を選んでクリックする」といったGUI部品を操作するやりかたです。これはこれで便利ではあるものの、スピードが(1)にくらべて100倍以上遅いですし、毎回確実に動くAppleScriptをこの方法で書くのは、けっこう難しいです。
いまだと、システム設定.appを操作していろんなシステムの状態を変更するようなAppleScriptを動かしたいと考えているユーザーさんがけっこういて、そのための方法をいろいろ探してはいるんですけれど、システム設定.appで細部まですべて操作するのはかなり無理があるので(iOSっぽい作り方になったことが災いして、GUI部品の階層を追いきれない)、この(2)の方法があるからできるっしょ! と、安易に考えないほうがいいです。
先日、X(旧Twitter)上でMacにつながっている複数のディスプレイのうち、メインディスプレイを切り替えたり、画面の解像度を切り替えたいという人がいて、ちょっとやりとりしました。その彼は、この(2)の方法でなんとかなるんじゃないかと考えていたようですが、実際に自分でやってみたところ、それはできませんでした。
なので、かつて調べてあった「SwitchResX」を用いる記事を紹介したところ、「有料アプリを買うほどではない」とのことでした。事実上、SwitchResXを使う方法でしか実現できないのですが、どうするんでしょう?
shell commandなどでディスプレイ解像度を取得・変更するものがあれば、それを呼び出すことになります。
macOSにインストールされているフォントの情報をsystem_profiler経由で取得し、各フォントのパス情報をもとに、/System/Library/Fonts以下に入っているものだけを抽出して、フォント名をリストアップするAppleScriptです。
Pages書類に含まれているフォントのうち、デフォルトインストールされたものではないものを抽出するために、OSにデフォルトインストールされたものだけを取得すべく、基礎情報を得るために書いてみたものです(現在執筆中の電子書籍「Pages+AppleScriptで本をつくろう!」の付録Scriptとして用意しました)。
事前に何か設定するといった必要はありません。スクリプトエディタやScript Debuggerで実行するだけです。
AppleScriptからフォントの存在するパス情報はなかなか取得しにくい、FontBook.appがAppleScript非対応になったので、ほぼ無理なのですが、system_profilerから取得すれば、それほど難しくありません。
実行所要時間は、
M1 Mac mini (macOS 13.6):6秒
M2 Mac mini(macOS 14.6):10秒
M2 MacBook Air(macOS 14.6):15秒
ぐらいで、M1 Mac miniの速さが光ります。OSの違いから、macOS 13.6環境ではallFontsが1168、macOS 14.6環境では1603とフォント数がどうやら異なっているのですが(M2 miniも同じ)、フォント数を考慮してもM1 Mac miniの処理の速さが光ります。
もしくは、macOSのバージョンの違いにより、system_profilerの実行速度が大きく異なるとか? macOS 13.6環境と14.6環境にそれほど差があるものなんでしょうか。
無駄な処理も入っていますが、いろいろチェックしながら作ったもので、ベンチマーク用に作ったものではありません。
# M2 Airってなんでこんなに時間がかかるんだろう? 放熱台に乗せて処理しているのに。処理のほとんどの時間がsystem_profilerの実行で消費しています
AppleScript名:OSデフォルトインストールされたフォント名を取得.scpt |
— – Created by: Takaaki Naganoya – Created on: 2024/08/03 — – Copyright © 2024 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use scripting additions property NSArray : a reference to current application’s NSArray property NSString : a reference to current application’s NSString property NSPredicate : a reference to current application’s NSPredicate property NSMutableArray : a reference to current application’s NSMutableArray property NSMutableDictionary : a reference to current application’s NSMutableDictionary property NSPropertyListFormat : a reference to current application’s NSPropertyListFormat property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding property NSPropertyListImmutable : a reference to current application’s NSPropertyListImmutable property NSPropertyListSerialization : a reference to current application’s NSPropertyListSerialization script spd property sRes : missing value property aSource : missing value property bRes : missing value property fontNames : {} property fontPath : {} property outList : {} end script –システムにインストールされているフォントの情報を全抽出 set sRes of spd to do shell script "system_profiler -xml SPFontsDataType" set aSource of spd to first item of (readPlistFromStr(sRes of spd) of me) –全フォント情報から、名称とパス情報を抽出 set bRes of spd to (((aSource of spd)’s valueForKeyPath:"_items")) set fontNames of spd to ((bRes of spd)’s valueForKeyPath:"_name") as list set fontPath of spd to ((bRes of spd)’s valueForKeyPath:"path") as list –ループでOSデフォルトインストールされているフォントを抽出 set outList of spd to {} set aCount to 1 repeat with i in (fontPath of spd) set j to contents of i if j begins with "/System/Library/Fonts/" then –指定のフォントに複数のTypefaceが含まれていることを考慮し、情報を取り出す set outItem to (((item aCount of (bRes of spd)))’s valueForKeyPath:"typefaces._name") as list set the end of (outList of spd) to outItem end if set aCount to aCount + 1 end repeat –すべてのTypefaceを入れた入れ子の2D Listから1D Listに変換 set allFonts to FlattenList((outList of spd)) of me –ドット(.)ではじまるフォントを除外 set outList to {} repeat with i in allFonts set j to contents of i if j does not start with "." then set the end of outList to j end if end repeat return outList –stringのplistを読み込んでRecordに on readPlistFromStr(theString) set aSource to NSString’s stringWithString:theString set pListData to aSource’s dataUsingEncoding:(NSUTF8StringEncoding) set aPlist to NSPropertyListSerialization’s propertyListFromData:pListData mutabilityOption:(NSPropertyListImmutable) format:(NSPropertyListFormat) errorDescription:(missing value) return aPlist end readPlistFromStr –リストに入れたレコードを、指定の属性ラベルの値で抽出 on filterRecListByLabel(aRecList as list, aPredicate as string) set aArray to NSArray’s arrayWithArray:aRecList set aPredicate to NSPredicate’s predicateWithFormat:aPredicate set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate set bList to filteredArray as list return bList end filterRecListByLabel –リストに入れたレコードを、指定の属性ラベルの値で抽出 on filterRecListByLabel1(aRecList, aPredicate as string) 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 return filteredArray end filterRecListByLabel1 –By Paul Berkowitz –2009年1月27日 2:24:08:JST –Re: Flattening Nested Lists on FlattenList(aList) set oldDelims to AppleScript’s text item delimiters set AppleScript’s text item delimiters to {"????"} set aString to aList as text set aList to text items of aString set AppleScript’s text item delimiters to oldDelims return aList end FlattenList |
Myriad Tables Lib(最新版はv1.13)はShane StanleyによるAppleScriptライブラリで、かつてのClassic MacOSに存在していたGUI記述系のOSAX(名前は忘れた)を思わせるほど柔軟性が高いAppleScriptライブラリの逸品です。
自分としては、これを用いる場合でも過度な作り込みを行わず、編集可能なデータビューワー的な機能を手軽に追加するのがよいと感じています。
自分でもさらに機能を簡略化した「display table by list」ライブラリを用意しています。
ところが、本ライブラリが機能しないケースが確認されました。
特定の呼び方を行うと、ダイアログ+表の表示そのものが封じられるという、macOS側の新たな制限によるもののようです。
そのため、自分がmacOS 10.12用にAppleScriptで開発したシステムが、現在のmacOS 14.xで部分的に動かなくなってしまいました。目下、システムを改修中です。Myriad Tables Libを使って手抜きをしていた箇所を、すべて自力で表UI(NSTableView)を使って表示・編集する必要があるようです(けっこう大変)。
同システムはステータスメニューからタイマー機能を呼び出すようなシステムだったのですが、このメニューからMyriad Tables Libを呼び出すと、Xcode上で作成したAppleScriptアプリであっても、Script Debugger上で実行したAppleScriptであっても、表示がブロックされてしまいます。
ランタイム環境がどれであっても、ステータスメニューから呼び出してMyriad Tables Libによるテーブル表示は行えませんでした。
macOS 10.12:可能
macOS 10.13:不可
macOS 10.14:(未検証)
macOS 10.15:(未検証)
macOS 11.x:(未検証)
macOS 12.x:(未検証)
macOS 13.6:不可
macOS 14.6:不可
macOS 15β:(未検証)
これは、Script Debugger上で実行したAppleScriptから、ステータスメニュー上に動的に項目を作成し、そこにメニューを追加した場合でも、Xcode上で作成した場合でも同様の挙動が見られます。
その後、Shaneに実際に聞いてみたところ、ダイアログ表示をメインスレッド実行しないとできないらしい(意訳)ことが明らかになってきました。
ダメ元で表UIつきダイアログ表示を明示的にメインスレッド指定して実行してみたところ、ハネられました。
自作の「display table by list」ライブラリを同様に呼び出してみたところ、これも表示できませんでした。このライブラリは自分が作ったのでソースコードをすべて自分が管理しており、アラートダイアログの表示ハンドラをメインスレッド指定で呼び出しています。どの時点でメインスレッド指定実行しないとダメなのかは明確ではありませんが、割と八方ふさがりな状況です。
メキシカンハットの描画AppleScriptのedama2さんバージョンで、さらに多色対応したものです。
AppleScript名:メキシカンハット v3a.scptd |
— Created 2024-06-28 by Takaaki Naganoya — Modified 2024-07-09 by Edama2 use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" use mLib : script "calcLibAS" use imgDispLib : script "imageDisplayLib" on run my main() end run on main() (* set userReply to display dialog "拡大率を入力してください(1-8)" default answer "3" set aScale to userReply’s text returned as number if aScale < 1 or 8 < aScale then return beep *) set aScale to 3 –描画 set listBezierPaths to {} repeat 3 times set listBezierPaths’s end to current application’s NSBezierPath’s bezierPath() end repeat –https://x.com/yone/status/1805552100400939161 set D to {} set dR to pi / 180 repeat 255 times set end of D to 192 end repeat repeat with y from -120 to 120 by 4 if y < 0 then set rY to -60 else set rY to 60 end if repeat with x from -180 to 180 by 4 if (x < -60) then set rX to -120 else if (x < 60) then set rX to 0 else set rX to 120 end if set rR to (x – rX) ^ 2 + (y – rY) ^ 2 set z to 180 * (exp (rR / -800)) set sX to floor (128 + x / 2 – y / 4) set sY to floor (128 – y / 4 – z / 2) if not (sX < 0 or 256 ≤ sX) then if not ((D’s item (sX + 1)) ≤ sY) then set zz to (floor (z * 0.0389)) + 1 log result set idRect to current application’s NSMakeRect(sX * aScale, sY * aScale, aScale, aScale) if ((zz = 1) or (zz = 3) or (zz = 5) or (zz = 7)) then (listBezierPaths’s item 1’s appendBezierPathWithRect:idRect) end if if ((zz = 2) or (zz = 3) or (zz ≥ 6)) then (listBezierPaths’s item 2’s appendBezierPathWithRect:idRect) end if if (zz ≥ 4) then (listBezierPaths’s item 3’s appendBezierPathWithRect:idRect) end if set (D’s item (sX + 1)) to sY end if end if end repeat end repeat # set srcWidth to 0 set srcHeight to 0 repeat with idBezierPath in listBezierPaths set tmpWidth to current application’s NSMaxX(idBezierPath’s |bounds|()) set tmpHeight to current application’s NSMaxY(idBezierPath’s |bounds|()) if srcWidth < tmpWidth then set srcWidth to tmpWidth if srcHeight < tmpHeight then set srcHeight to tmpHeight end repeat # set srcSize to current application’s NSMakeSize(srcWidth, srcHeight) log result set srcImage to current application’s NSImage’s alloc()’s initWithSize:srcSize set operation to current application’s NSCompositingOperationScreen –>set operation to current application’s NSCompositingOperationSourceOver repeat with num from 1 to count listBezierPaths if num is 1 then set idColor to current application’s NSColor’s blueColor() else if num is 2 then set idColor to current application’s NSColor’s redColor() else if num is 3 then set idColor to current application’s NSColor’s greenColor() end if tell srcImage lockFocus() tell current application’s NSImage’s alloc() tell initWithSize_(srcSize) lockFocus() idColor’s setFill() listBezierPaths’s item num’s fill() unlockFocus() (its drawAtPoint:(current application’s NSZeroPoint) fromRect:(current application’s NSZeroRect) operation:operation fraction:1.0) end tell end tell unlockFocus() end tell end repeat # 反転して合成 set dstImage to current application’s NSImage’s alloc()’s initWithSize:srcSize tell dstImage lockFocus() current application’s NSColor’s blackColor()’s setFill() current application’s NSRectFill(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height)) set xform to current application’s NSAffineTransform’s transform() xform’s translateXBy:0.0 yBy:(its |size|()’s height) xform’s scaleXBy:1.0 yBy:-1.0 xform’s concat() srcImage’s drawInRect:(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height)) unlockFocus() end tell –結果表示 dispImage(dstImage, "Mexican Hat") of imgDispLib end main |
メキシカンハットの描画AppleScriptの、edama2さんバージョンです。
AppleScript名:メキシカンハット v3.scptd |
— Created 2024-06-28 by Takaaki Naganoya — Modified 2024-07-09 by Edama2 use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" use mLib : script "calcLibAS" use imgDispLib : script "imageDisplayLib" on run my main() end run on main() (* set userReply to display dialog "拡大率を入力してください(1-8)" default answer "3" set aScale to userReply’s text returned as number if aScale < 1 or 8 < aScale then return beep *) set aScale to 3 –描画 set idBezierPath to current application’s NSBezierPath’s bezierPath() –https://x.com/Stosstruppe/status/1279774386396880897 set dT to {} set dB to {} repeat 256 times set dT’s end to 192 end repeat repeat 256 times set dB’s end to -1 end repeat repeat with y from -120 to 120 by 4 if y < 0 then set rY to -60 else set rY to 60 end if repeat with x from -180 to 180 by 4 if (x < -60) then set rX to -120 else if (x < 60) then set rX to 0 else set rX to 120 end if set rR to (x – rX) ^ 2 + (y – rY) ^ 2 set z to 180 * (exp (rR / -800)) set cX to floor (128 + x / 2 – y / 4) set cY to floor (128 – y / 4 – z / 2) if not (cX < 0 or 256 ≤ cX) then set pSet to false if ((dT’s item (cX + 1)) > cY) then set (dT’s item (cX + 1)) to cY set pSet to true end if if ((dB’s item (cX + 1)) < cY) then set dB’s item (cX + 1) to cY set pSet to true end if if pSet then set idRect to current application’s NSMakeRect(cX * aScale, cY * aScale, aScale, aScale) (idBezierPath’s appendBezierPathWithRect:idRect) end if end if end repeat end repeat log idBezierPath’s |bounds|() set srcWidth to current application’s NSMaxX(idBezierPath’s |bounds|()) set srcHeight to current application’s NSMaxY(idBezierPath’s |bounds|()) set srcSize to current application’s NSMakeSize(srcWidth, srcHeight) log result –set idColor to current application’s NSColor’s blueColor() –set idColor to current application’s NSColor’s redColor() set idColor to current application’s NSColor’s greenColor() tell current application’s NSImage’s alloc() tell initWithSize_(srcSize) lockFocus() idColor’s setFill() idBezierPath’s fill() unlockFocus() set srcImage to it end tell end tell # 反転して合成 set dstImage to current application’s NSImage’s alloc()’s initWithSize:srcSize tell dstImage lockFocus() current application’s NSColor’s blackColor()’s setFill() current application’s NSRectFill(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height)) set xform to current application’s NSAffineTransform’s transform() xform’s translateXBy:0.0 yBy:(its |size|()’s height) xform’s scaleXBy:1.0 yBy:-1.0 xform’s concat() srcImage’s drawInRect:(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height)) unlockFocus() end tell –結果表示 dispImage(dstImage, "Mexican Hat") of imgDispLib end main |
以前掲載したメシキカンハット描画AppleScriptが、Retinaディスプレイ環境で表示が崩れていたため、修正してみました。
実行は、以下のScript Bundleをダウンロードして行なってください。
▲非Retina環境のMacで実行したところ(M1 Mac mini)
▲Retina環境のMacで実行したところ(M2 MacBook Air)
AppleScript名:メキシカンハット v2a.scptd |
— Created 2024-06-28 by Takaaki Naganoya — Modified 2024-07-28 by Takaaki Naganoya use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" use mLib : script "calcLibAS" use imgDispLib : script "imageDisplayLib" property NSColor : a reference to current application’s NSColor property NSString : a reference to current application’s NSString property NSImage : a reference to current application’s NSImage property NSScreen : a reference to current application’s NSScreen property NSBezierPath : a reference to current application’s NSBezierPath property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep property retinaF : 1.0 set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real –> 2.0 (Retina) / 1.0 (Non Retina) –パラメータから下地になる画像を作成する set aSize to current application’s NSMakeSize(300, 200) set anImage to NSImage’s alloc()’s initWithSize:aSize set backlColor to (NSColor’s colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:1.0) drawImageWithColorFill(anImage, {0, 0, 300, 200}, backlColor) of me –描画色 set drawColor to (NSColor’s colorWithCalibratedRed:0.0 green:1.0 blue:0.0 alpha:1.0) –Mexican Hat set D to {} set rDnum to pi / 180 repeat 256 times set end of D to 100 end repeat repeat with y from -180 to 180 by 6 repeat with x from -180 to 180 by 4 set u to 120 + (x / 3) – (y / 6) if u < 0 or u ≥ 240 then — else set r to rDnum * (sqrt ((x ^ 2) + (y ^ 2))) set z to 100 * (cos (r – 30)) * (cos (3 * r)) set v to 40 – y / 6 – z / 2 if (item u of D) > v then set anImage to psetOnImage(anImage, {(u + 20) * retinaF, (120 – v) * retinaF}, drawColor) of me copy v to (item u of D) end if end if end repeat end repeat –結果表示 dispImage(anImage, "Mexican Hat") of imgDispLib on psetOnImage(anImage, posDat, fillColor) set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real –> 2.0 (Retina) / 1.0 (Non Retina) anImage’s lockFocus() –描画開始 set origX to (item 1 of posDat) / retinaF set origY to (item 2 of posDat) / retinaF set sizeX to (1) * retinaF set sizeY to (1) * retinaF set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}} set theNSBezierPath to NSBezierPath’s bezierPath (theNSBezierPath’s appendBezierPathWithRect:theRect) fillColor’s |set|() –色設定 theNSBezierPath’s fill() –ぬりつぶし anImage’s unlockFocus() –描画ここまで return anImage –returns NSImage end psetOnImage on drawImageWithColorFill(anImage, drawList, fillColor) set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real –> 2.0 (Retina) / 1.0 (Non Retina) anImage’s lockFocus() –描画開始 set origX to (item 1 of drawList) –* retinaF set origY to (item 2 of drawList) –* retinaF set sizeX to (item 3 of drawList) –* retinaF set sizeY to (item 4 of drawList) –* retinaF set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}} set theNSBezierPath to NSBezierPath’s bezierPath (theNSBezierPath’s appendBezierPathWithRect:theRect) fillColor’s |set|() –色設定 theNSBezierPath’s fill() –ぬりつぶし anImage’s unlockFocus() –描画ここまで –return anImage –returns NSImage end drawImageWithColorFill |
AppleScriptでYouTubeのPicture In Pictureウィンドウを操作したいという話を見かけて、そんなことは一度も考えたことがなかったので調べてみました。macOS 10.13あたりで搭載されたPicture In Pictureのムービー再生機能をAppleScriptから操作します。
結論からいえば、Picture In PictureはmacOS側が用意している専用のプログラム「PIPAgent」(/System/Library/CoreServices/PIPAgent.app)がPicture In Pictureの処理を行っています。Webブラウザの内蔵機能ではなく、各ブラウザで共通してこれを呼び出すようです。
Picture In Picture機能は、たとえばSafariでYouTubeムービーを再生した状態で、マウスの右クリックをムービー上で2回行うことで、その機能が呼び出せます。
▲YouTubeムービーの上でマウスの右クリックを行なった状態(1回目)
▲YouTubeムービーの上でマウスの右クリックを行なった状態(2回目)。ここで「ピクチャインピクチャにする」を実行するとPIP表示になる
プロセス一覧で「それっぽい名前」のプロセスに当たりをつけてGUI Scriptingからウィンドウを操作すれば、ウィンドウの位置と大きさをAppleScriptから変更できました。
ただし、このPIPAgentのウィンドウは画面のすみにしか置けませんし、サイズについてもいくつかのサイズを指定できても細かい大きさは指定できません。ここで紹介するAppleScriptも、自分の環境(1920×1200)の画面上で試したものであり、複数の画面をつないでいたり、2x Retinaや3x Retinaなどの解像度の画面をつないである場合には、座標値を書き換えたほうがよいでしょう(あくまで、実験レベルのコードです)。
PIPAgentのウィンドウの縦横比はオリジナルのムービーのものが維持されるため、AppleScriptでサイズを指定しても縦横比が崩れることはありません。
なお、「ピクチャインピクチャ」表示状態にするまで、PIPAgent.appは起動しません。同プロセスの起動を確認したうえでウィンドウの制御を行う必要があります。プロセス確認については、山のように方法がありますのでお好きなように。
AppleScript名:YouTube Picture In Pictureウィンドウの制御.scpt |
— – Created by: Takaaki Naganoya – Created on: 2024/07/22 — – Copyright © 2024 Piyomaru Software, All Rights Reserved — tell application "Safari" to activate tell application "System Events" try tell process "PIPAgent" set wCount to unix id end tell on error return –YouTubeのPicture In Picture表示プロセスが起動していない end try tell process "PIPAgent" set wCount to count every window if wCount = 0 then return tell window 1 set size to {500, 282} repeat 10 times set position to {1493, 800} delay 3 set position to {0, 800} delay 3 set position to {0, 0} delay 3 set position to {1493, 0} delay 3 end repeat end tell end tell end tell |
日本のカレンダーにおける国民の祝日(休日)を、dateオブジェクトのリストで求めるAppleScriptです。
2020〜2021年のイレギュラーな休日変更に対応してみました。
# 何か不具合や不足分があったらおしらせください
# 某・呪われた手帳メーカー系の仕事には不十分かもしれないので、不幸にもその案件に関わった方はメーカー支給の祝祭日データを利用してください
こうした高度なカレンダー計算AppleScriptは、「Newt On Project」の直系の遺産です。自然言語で曜日や日数の指定を行う上で、休日の計算は欠かせないものでした。
休日に関しては政府がCSVファイルで配布しているものもありますが、来年や再来年、5年後のカレンダーを扱いたいといった場合に、自前で計算できないと話になりません。そうした処理に備える意味でも、自前で計算できる「意義」はあります。
AppleScript名:国民の祝日を求める v7.scpt |
use AppleScript use scripting additions use framework "Foundation" (* 本バージョンのテーマ: *) set aList to retHolidayRec(2020) of me–> {{date "2020年1月1日 水曜日 0:00:00", "元旦"}, {date "2020年1月13日 月曜日 0:00:00", "成人の日"}, {date "2020年2月11日 火曜日 0:00:00", "建国記念日"}, {date "2020年2月23日 日曜日 0:00:00", "天皇誕生日"}, {date "2020年2月24日 月曜日 0:00:00", "振替休日"}, {date "2020年3月20日 金曜日 0:00:00", "春分の日"}, {date "2020年5月3日 日曜日 0:00:00", "憲法記念日"}, {date "2020年5月4日 月曜日 0:00:00", "みどりの日"}, {date "2020年5月5日 火曜日 0:00:00", "こどもの日"}, {date "2020年5月6日 水曜日 0:00:00", "振替休日"}, {date "2020年7月22日 水曜日 0:00:00", "海の日"}, {date "2020年7月24日 金曜日 0:00:00", "スポーツの日"}, {date "2020年8月10日 月曜日 0:00:00", "山の日"}, {date "2020年9月21日 月曜日 0:00:00", "敬老の日"}, {date "2020年9月22日 火曜日 0:00:00", "秋分の日"}, {date "2020年11月3日 火曜日 0:00:00", "文化の日"}, {date "2020年11月23日 月曜日 0:00:00", "勤労感謝の日"}} –国民の祝日を求める(属性つき) on retHolidayRec(aYear as integer) –固定の祝日 if aYear < 2020 then set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"4/29", "昭和の日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}} else if aYear = 2020 then set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}} else if aYear = 2021 then set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}} else –2022年から set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}} end if –天皇誕生日の計算 set curEmpBD to getCurEmpBitrthday(aYear) of me if curEmpBD is not equal to false then set the end of holidayList to {curEmpBD, "天皇誕生日"} end if set the end of holidayList to {get_specifiedDay(aYear, 1, 2, 2), "成人の日"} –成人の日–1月の第2月曜日 –令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について if aYear = 2020 then set the end of holidayList to {"7/ 22", "海の日"} else if aYear = 2021 then set the end of holidayList to {"7/ 23", "海の日"} else set the end of holidayList to {get_specifiedDay(aYear, 7, 2, 3), "海の日"} –海の日–7月の第3月曜日 end if set the end of holidayList to {get_specifiedDay(aYear, 9, 2, 3), "敬老の日"} –敬老の日–9月の第3月曜日 –令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について if aYear = 2020 then set the end of holidayList to {"7/24", "スポーツの日"} else if aYear = 2021 then set the end of holidayList to {"7/23", "スポーツの日"} else if (aYear < 2000) then set the end of holidayList to {"10/10", "体育の日"} –体育の日–10月10日 else if (aYear < 2020) then set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "体育の日"} –体育の日–10月の第2月曜日 else set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "スポーツの日"} –スポーツの日–10月の第2月曜日 end if –令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について if aYear = 2020 then set the end of holidayList to {"8/10", "山の日"} else if aYear = 2021 then set the end of holidayList to {"8/8", "山の日"} else if aYear ≥ 2016 then set the end of holidayList to {"8/11", "山の日"} –山の日– 8月11日 end if set the end of holidayList to {"3/" & get_ShunbunNoHi(aYear), "春分の日"} –春分の日 set the end of holidayList to {"9/" & get_ShuubunNoHi(aYear), "秋分の日"} –秋分の日 set holiDate to {} repeat with i in holidayList set holiD to date (aYear & "/" & (item 1 of i) as text) set holiNum to weekday of holiD as number –元日以外を対象とする(元旦に振替休日なし)–> いや、ある(汗) –if ((item 1 of i) as text) is not "1/1" then –振替休日付加処理 if holiNum = 1 then –祝祭日が日曜日だったら –日付を動かすのではなく、振替休日を追加する set holiD_furikae to holiD + (1 * days) set the end of holiDate to {holiD_furikae, "振替休日"} end if –end if set the end of holiDate to {holiD, item 2 of i} end repeat –重複した休日が発生した場合の再振替処理 –基本ルール: 振替休日を後に送る – 「振替休日」が重複リストに入っていないかどうかをチェックし、振替休日の再配置を行う set itemNum to 1 set holiDateDup to detectDuplicatesFromNestedList(holiDate, itemNum) of me set huriList to {} repeat with i in holiDateDup set iCount to length of i repeat with ii in i set {aDate, aDateName} to ii if aDateName = "振替休日" then set the end of huriList to contents of ii end if end repeat end repeat set holiDate to shellSortListAscending(holiDate, 1) of me set holiDateList to spritOrderedItemFromNestedList(holiDate, 1) of me repeat with i in huriList set {aDate, aName} to i set j to contents of i set offsetDate to 1 repeat set bDate to aDate + (offsetDate * days) if bDate is not in holiDateList then exit repeat end if set offsetDate to offsetDate + 1 end repeat set iCount to 1 repeat with ii in holiDate set jj to contents of ii if jj = j then –「複数要素一括削除サブルーチン」などという高機能すぎるサブルーチンを使用。ちょっともったいない set holiDate to itemsDelete(holiDate, {iCount}) of me end if set iCount to iCount + 1 end repeat set the end of holiDate to {bDate, "振替休日"} end repeat –秋分の日と敬老の日の「間の日」の休日判定処理 –参考文献: –http://ja.wikipedia.org/wiki/秋分の日 –国民の祝日に関する法律第3条第3項に規定する休日(例) set septDL to {} set the end of septDL to "9/" & get_ShuubunNoHi(aYear) of me –秋分の日 set the end of septDL to get_specifiedDay(aYear, 9, 2, 3) –敬老の日 –9月の第3月曜日 set septDL to shellSort(septDL) of me if septDL = {"9/21", "9/23"} then set kokuminShukujitu to (aYear as string) & "/9/22" set kokuminShukujitu to date kokuminShukujitu set the end of holiDate to {kokuminShukujitu, "国民の祝日"} end if –重複を解消 set holiDate to removeDuplicates(holiDate) of me –最後に、並べ替えを行って仕上げ set holiDate to shellSortListAscending(holiDate, 1) of me return holiDate end retHolidayRec –春分の日を求める –2000年から2099年の間まで計算可能 on get_ShunbunNoHi(aYear) set a to 20.69115 set b to (aYear – 2000) * 0.2421904 set c to round ((aYear – 2000) / 4) rounding toward zero set d to round (a + b – c) rounding toward zero return d end get_ShunbunNoHi –秋分の日を求める –2000年から2099年の間まで計算可能 on get_ShuubunNoHi(aYear) set a to 23.09 set b to (aYear – 2000) * 0.2421904 set c to round ((aYear – 2000) / 4) rounding toward zero set d to round (a + b – c) rounding toward zero return d end get_ShuubunNoHi –指定月の第x指定曜日に該当する日付を求める(mm/dd形式) – 曜日の指定を数値(weekday of (current date) as number)で行えるようにした。 – 曜日を「日曜日」などの日本語ローカライズド文字列で指定するのをやめた –パラメータ: 年, 月, 曜日番号, 順番 on get_specifiedDay(aYear as integer, aMonth as integer, Youbi as integer, orderNum as integer) set sDat to date ((aYear & "/" & aMonth & "/1") as text) set eDat to getMlenInternational(aYear, aMonth) of me set countNum to 0 repeat with i from 1 to eDat set aCal to date ((aYear & "/" & aMonth & "/" & (i as text)) as text) set aWeekDayNum to weekday of aCal as integer if Youbi = aWeekDayNum then set countNum to countNum + 1 if countNum is orderNum then set aCalText to (aMonth & "/" & i as text) return aCalText end if end if end repeat end get_specifiedDay –指定日の月のみ返す on getMonth(aDat as date) set bDate to month of aDat return bDate as integer end getMonth –指定日の日付のみ返す on getDate(aDat as date) set bDate to day of aDat return bDate as integer end getDate –指定日の年のみ返す on getYear(aDat as date) set bDate to year of aDat return bDate as integer end getYear –現在のカレンダーで指定年月の日数を返す(getMlenから置き換えた) on getMlenInternational(aYear, aMonth) set theNSCalendar to current application’s NSCalendar’s currentCalendar() — do *not* use initWithCalendarIdentifier: set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0 set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate return |length| of theResult end getMlenInternational –リスト中から重複項目をリストアップする on detectDuplicates(aList) set aCount to length of aList set duplicationList to {} repeat aCount times set anItem to contents of (first item of aList) set aList to rest of aList if anItem is in aList then set the end of duplicationList to anItem end if end repeat return duplicationList end detectDuplicates –リストから重複部分を除外 on removeDuplicates(aList) set newList to {} repeat with i from 1 to (length of aList) set anItem to item 1 of aList set aList to rest of aList if {anItem} is not in aList then set end of newList to anItem end repeat return newList end removeDuplicates –シェルソート on shellSort(aSortList) script oBj property list : aSortList end script set len to count oBj’s list’s items set gap to 1 repeat while (gap ≤ len) set gap to ((gap * 3) + 1) end repeat repeat while (gap > 0) set gap to (gap div 3) if (gap < len) then repeat with i from gap to (len – 1) set temp to oBj’s list’s item (i + 1) set j to i repeat while ((j ≥ gap) and (oBj’s list’s item (j – gap + 1) > temp)) set oBj’s list’s item (j + 1) to oBj’s list’s item (j – gap + 1) set j to j – gap end repeat set oBj’s list’s item (j + 1) to temp end repeat end if end repeat return oBj’s list end shellSort –シェルソートで入れ子のリストを昇順ソート on shellSortListAscending(a, keyItem) set n to length of a set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1} repeat with h in cols if (h ≤ (n – 1)) then repeat with i from h to (n – 1) set v to item (i + 1) of a set j to i repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) > (item keyItem of v)) set (item (j + 1) of a) to (item (j – h + 1) of a) set j to j – h end repeat set item (j + 1) of a to v end repeat end if end repeat return a end shellSortListAscending –シェルソートで入れ子のリストを降順ソート on shellSortListDecending(a, keyItem) set n to length of a set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1} repeat with h in cols if (h ≤ (n – 1)) then repeat with i from h to (n – 1) set v to item (i + 1) of a set j to i repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) < (item keyItem of v)) set (item (j + 1) of a) to (item (j – h + 1) of a) set j to j – h end repeat set item (j + 1) of a to v end repeat end if end repeat return a end shellSortListDecending –入れ子のリスト中から重複項目をアイテム番号つきでリストアップする on detectDuplicatesFromNestedList(aList, itemNum) set aCount to length of aList copy aList to orig_aList set duplicationList to {} repeat aCount times set anItem to contents of (first item of aList) set aList to rest of aList –指定アイテムだけのリストを毎回再生成して存在確認を行う set aaList to spritOrderedItemFromNestedList(aList, itemNum) of me if (contents of (item itemNum of anItem)) is in aaList then set the end of duplicationList to anItem end if end repeat –検出した重複データを元に、該当するデータをリストアップ set detectList to {} repeat with i in duplicationList set j to contents of (item itemNum of i) set detectItem to {} repeat with ii in orig_aList set jj to contents of (item itemNum of ii) if jj = j then set the end of detectItem to (contents of ii) end if end repeat set the end of detectList to detectItem end repeat return detectList end detectDuplicatesFromNestedList –入れ子のリストの全要素から指定アイテム目の要素だけを取り出してリストで返す on spritOrderedItemFromNestedList(aList, itemNum) set aaList to {} repeat with i in aList set the end of aaList to contents of (item itemNum of i) end repeat return aaList end spritOrderedItemFromNestedList –リスト中の指定要素を削除して返す on itemsDelete(aList, delNumList) set delLen to length of delNumList repeat with i from 1 to delLen set newList to {} set aLen to length of aList set ii to item i of delNumList if ii = 1 then set maeList to items 2 thru aLen of aList set newList to maeList else if ii = aLen then set maeList to items 1 thru (aLen – 1) of aList set newList to maeList else set maeList to items 1 thru (ii – 1) of aList set atoList to items (ii + 1) thru -1 of aList set newList to maeList & atoList end if –アイテム指定の補正 set delNumList to adjustItemNo(ii, delNumList) of me set aList to newList end repeat return newList end itemsDelete –itemsDeleteのサブルーチン –リストに対して複数アイテムの削除を行う場合に、1つ削除した後にはアイテム指定が –狂ってしまうため、毎回削除するたびにアイテム指定の補正を行う –paramNumとelemListの間でのパラメータの衝突は関知しない –項目要素補正をリストに対して行う on adjustItemNo(paramNum, elemList) –項目ゼロを指定してきた場合には、そのままelemListを戻す if paramNum = 0 then return elemList –プラス方向のレンジ外判定は行っていない。elemListのlengthよりも大きな値は関知しない set retList to {} repeat with i in elemList set j to contents of i if j > paramNum then set ansNum to j – 1 else set ansNum to j end if set the end of retList to ansNum end repeat return retList end adjustItemNo –現在のカレンダーで指定年月のdate objectを返す(年、月、日、時、分、秒) on getDateInternationalYMDhms(aYear, aMonth, aDay, anHour, aMinute, aSecond) set theNSCalendar to current application’s 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 –現在のカレンダーで指定年月のdate objectを返す(年、月、日) on getDateInternational(aYear, aMonth, aDay) set theNSCalendar to current application’s NSCalendar’s currentCalendar() set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:0 minute:0 |second|:0 nanosecond:0 return theDate as date end getDateInternational –天皇誕生日の計算 on getCurEmpBitrthday(targYear) –昭和、平成、令和 の誕生日定義 set curEmperrorsBirthday to {{"4/29", {1926, 1988}}, {"12/23", {1989, 2018}}, {"2/23", {2020, 9999}}} –浩宮氏が崩御の際には、崩御年を記入 set hitF to false repeat with i in curEmperrorsBirthday copy i to {targDate, {beginYear, endYear}} if targYear ≥ beginYear and targYear ≤ endYear then set hitF to true exit repeat end if end repeat if hitF = false then return false end if return targDate end getCurEmpBitrthday |