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

タグ: 14.0savvy

YouTube Picture In Pictureウィンドウの制御

Posted on 7月 23, 2024 by Takaaki Naganoya

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

★Click Here to Open This Script 

Posted in GUI Scripting | Tagged 13.0savvy 14.0savvy 15.0savvy Safari | Leave a comment

国民の祝日を求める v7

Posted on 7月 17, 2024 by Takaaki Naganoya

日本のカレンダーにおける国民の祝日(休日)を、dateオブジェクトのリストで求めるAppleScriptです。

2020〜2021年のイレギュラーな休日変更に対応してみました。

# 何か不具合や不足分があったらおしらせください
# 某・呪われた手帳メーカー系の仕事には不十分かもしれないので、不幸にもその案件に関わった方はメーカー支給の祝祭日データを利用してください

こうした高度なカレンダー計算AppleScriptは、「Newt On Project」の直系の遺産です。自然言語で曜日や日数の指定を行う上で、休日の計算は欠かせないものでした。

休日に関しては政府がCSVファイルで配布しているものもありますが、来年や再来年、5年後のカレンダーを扱いたいといった場合に、自前で計算できないと話になりません。そうした処理に備える意味でも、自前で計算できる「意義」はあります。

AppleScript名:国民の祝日を求める v7.scpt
use AppleScript
use scripting additions
use framework "Foundation"

(*

本バージョンのテーマ:
東京オリンピック(2020/2021年)関連の超イレギュラーな休日調整に対応

*)

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

★Click Here to Open This Script 

Posted in Calendar | Tagged 10.15savvy 11.0savvy 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が

Posted on 7月 12, 2024 by Takaaki Naganoya

Pixelmator Proでたまに実行する「1024×1024の画像からアイコン用の各種解像度の画像を作成する」AppleScriptですが、Pixelmator Pro v3.6.4で久しぶりに動かしたら、エラーが出てうまく動かなくなっていました。

このAppleScriptは、1024×1024の画像をオープンした状態で実行するものです。

すぐに、各サイズにリサイズしつつ、ファイル名に解像度を反映させた状態で書き出しを行います。

AppleScript名:アイコン書き出しv2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/10/19
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

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

set resolList to {1024, 512, 256, 128, 64, 32, 16}
set aTargFileBase to (choose file name with prompt "Select Export base name") as string

tell application "Pixelmator Pro"
  if (exists of document 1) = false then
    display dialog "There is no document" buttons {"OK"} default button 1 with icon 1
    
return
  end if
  
  
tell front document
    set aWidth to width
    
set aHeight to height
    
    
if {aWidth, aHeight} is not equal to {1024.0, 1024.0} then
      display dialog "Wrong Image Size (1024×1024 required)" buttons {"OK"} default button 1 with icon 2 with title "Size Error"
      
return
    end if
    
    
repeat with i in resolList
      resize image width i height i resolution 72 algorithm bilinear
      
export to file (aTargFileBase & "_" & (i as string) & "x" & (i as string) & ".png") as PNG
      
undo
    end repeat
  end tell
end tell

★Click Here to Open This Script 

これが、macOS 13.6.8+Pixelmator Pro v3.6.4の組み合わせで実行したらエラーが出て実行できなくなっていました。

問題点は割と明らかです。documentへのtellブロック内で、documentをパラメータとして要求するexportといったコマンドを実行したときに、tellブロックを認識せず、コマンドの直後にdocumentオブジェクトを表記しないとエラーになります。

解決策は、tellブロックでdocumentを指定するのをやめて、コマンドのパラメータとしてfront documentといったオブジェクトを表記することです。または、「属性値 of it」のようにdocumentへの参照を記述しておくことです(他の言語のユーザーが腰を抜かすので、なるべくitは使わないで記述していますが……)。

これで解決できるものの、macOS側で問題を起こしているのか、Pixelmator Pro側で問題を起こしているのかがわかりにくいところ。

おそらく、Pixelmator Pro側の問題かと思われますが、言い切ることもできないでしょう。

AppleScript名:アイコン書き出しv3.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/10/19
–  Modified on: 2024/07/11
—
–  Copyright © 2020-2024 Piyomaru Software, All Rights Reserved
—

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

set resolList to {1024, 512, 256, 128, 64, 32, 16}
set aTargFileBase to (choose file name with prompt "Select Export base name") as string

tell application "Pixelmator Pro"
  if (exists of document 1) = false then
    display dialog "There is no document" buttons {"OK"} default button 1 with icon 1
    
return
  end if
  
  
tell the front document
    set aWidth to width
    
set aHeight to height
  end tell
  
  
if {aWidth, aHeight} is not equal to {1024.0, 1024.0} then
    display dialog "Wrong Image Size (1024×1024 required)" buttons {"OK"} default button 1 with icon 2 with title "Size Error"
    
return
  end if
  
  
repeat with i in resolList
    resize image front document width i height i resolution 72 algorithm bilinear
    
export front document to file (aTargFileBase & "_" & (i as string) & "x" & (i as string) & ".png") as PNG
    
undo
  end repeat
  
end tell

★Click Here to Open This Script 

Posted in Bug Object control | Tagged 13.0savvy 14.0savvy 15.0savvy Pixelmator Pro | Leave a comment

Safariで「プロファイル」機能を使うとAppleScriptの処理に影響

Posted on 7月 6, 2024 by Takaaki Naganoya

Safariの最近のバージョンで導入されたものの、使わないでいた「プロファイル」機能。

Appleが提案してきた鳴り物入りの新機能のうち、ほとんどのものはその日のうちにオフにしていたり、「なかったもの」にしているものが(個人的に)多いです。最新のものでは、macOS 15.0の「ウィンドウの吸着機能」は、その日のうちにオフにしました。

「プロファイル機能」も、そんな「Appleが作った新たなゴミ機能」だと思っていたのですが、よくよく調べると、

拡張機能やクッキーなど環境情報をプロファイルごとに切り替えられるようになっています。

Safariを用いたプログラムの開発を行う必要があり、この「プロファイル」機能に注目。個人環境と開発環境を区分けできることにメリットを感じて使いはじめました。

ところが、AppleScriptでSafariのウィンドウのタイトルを取得する処理を実行すると、

AppleScript名:最前面のウィンドウのタイトルを取得する.scpt
set aTitle to getPageTitleOfFrontmostWindow()
–> "個人用 ― AppleScriptの穴 – Useful & Practical AppleScript archive. Click ’★Click Here to Open This Script’ Link to download each AppleScript"

on getPageTitleOfFrontmostWindow()
  tell application "Safari"
    if (count every window) = 0 then return false
    
tell window 1
      set aProp to properties
    end tell
    
set aDoc to document of aProp
    
set aText to name of aDoc
  end tell
  
return aText
end getPageTitleOfFrontmostWindow

★Click Here to Open This Script 

タイトルの前にプロファイル名がついてきてしまいます。これは、嬉しくない状況です。

現在のプロファイル名を取得できるとか、使用プロファイルを指定できるとかいった機能なら前向きなのですが……。

JavaScript経由でページのタイトルを取得したところ、プロファイル名はついてきませんでした。

この2つの方法を試すことで、どちらか最適な方を選ぶとか、両方を組み合わせてプロファイル名を抽出するといった処理を行うべきでしょう。

AppleScript名:最前面のウィンドウのタイトルを取得する(JS).scpt
set aTitle to getPageTitleOfFrontmostWindowByJS()
–> "AppleScriptの穴 – Useful & Practical AppleScript archive. Click ’★Click Here to Open This Script’ Link to download each AppleScript"

on getPageTitleOfFrontmostWindowByJS()
  tell application "Safari"
    if (count every window) = 0 then return false
    
set aRes to do JavaScript "document.title;" in front document
    
return aRes
  end tell
end getPageTitleOfFrontmostWindowByJS

★Click Here to Open This Script 

Posted in Object control | Tagged 13.0savvy 14.0savvy 15.0savvy Safari | Leave a comment

国民の祝日を求める v6

Posted on 7月 5, 2024 by Takaaki Naganoya

日本のカレンダーにおける国民の祝日(休日)を、dateオブジェクトのリストで求めるAppleScriptです。

2018年に改元(平成→令和)、2020年に予定されていた東京オリンピックがCOVID-19の影響を受けて2021年に順延し、そのあたりで休日・祝日運用がイレギュラーになりまくって心が折れてアップデートしていませんでしたが、ようやく本ルーチンを最新の状況に合わせてアップデートしました。

2020〜2021年の計算については本ルーチンでは合わなくなる可能性がありますが、その点を理解したうえでご利用ください。一応、今年(2024年)の休日については、内閣府のホームページに掲載されているカレンダーと付け合わせを行なって、処理内容を確認してあります。

# 何か不具合や不足分があったらおしらせください
# 某・呪われた手帳メーカー系の仕事には不十分かもしれないので、不幸にもその案件に関わった方はメーカー支給の祝祭日データを利用してください

AppleScript名:国民の祝日を求める v6.scpt
use AppleScript
use scripting additions
use framework "Foundation"

(*

本バージョンのテーマ:
令和への改元にともない、天皇が交代。天皇誕生日を変更。祝日関連を最新アップデート
東京オリンピック(2021年)関連の超イレギュラーな休日調整は考慮していない

*)

set aList to retHolidayRec(2024) of me
–> {{date "2024年1月1日 月曜日 0:00:00", "元旦"}, {date "2024年1月8日 月曜日 0:00:00", "成人の日"}, {date "2024年2月11日 日曜日 0:00:00", "建国記念日"}, {date "2024年2月12日 月曜日 0:00:00", "振替休日"}, {date "2024年2月23日 金曜日 0:00:00", "天皇誕生日"}, {date "2024年3月20日 水曜日 0:00:00", "春分の日"}, {date "2024年5月3日 金曜日 0:00:00", "憲法記念日"}, {date "2024年5月4日 土曜日 0:00:00", "みどりの日"}, {date "2024年5月5日 日曜日 0:00:00", "こどもの日"}, {date "2024年5月6日 月曜日 0:00:00", "振替休日"}, {date "2024年7月15日 月曜日 0:00:00", "海の日"}, {date "2024年8月11日 日曜日 0:00:00", "山の日"}, {date "2024年8月12日 月曜日 0:00:00", "振替休日"}, {date "2024年9月16日 月曜日 0:00:00", "敬老の日"}, {date "2024年9月22日 日曜日 0:00:00", "秋分の日"}, {date "2024年9月23日 月曜日 0:00:00", "振替休日"}, {date "2024年10月14日 月曜日 0:00:00", "スポーツの日"}, {date "2024年11月3日 日曜日 0:00:00", "文化の日"}, {date "2024年11月4日 月曜日 0:00:00", "振替休日"}, {date "2024年11月23日 土曜日 0:00:00", "勤労感謝の日"}}

–国民の祝日を求める(属性つき)
on retHolidayRec(aYear as text)
  –固定の祝日
  
if aYear < 2020 then
    set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"4/29", "昭和の日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
  else
    –2020年から
    
set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"8/11", "山の日"}, {"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月曜日
  
set the end of holidayList to {get_specifiedDay(aYear, 7, 2, 3), "海の日"} –海の日–7月の第3月曜日
  
set the end of holidayList to {get_specifiedDay(aYear, 9, 2, 3), "敬老の日"} –敬老の日–9月の第3月曜日
  
set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "スポーツの日"} –体育の日–10月の第2月曜日
  
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/秋分の日
  
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 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

★Click Here to Open This Script 

Posted in Calendar | Tagged 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

相対年月を計算(月の相対指定)v4

Posted on 7月 5, 2024 by Takaaki Naganoya

現在日時からの相対月計算を行うAppleScriptです。

# 実戦投入してみたらマイナス月で不具合が出たので修正しました

カレンダー計算を行うのは、たいてい「日」単位です。

相対日では、今日(day ±0)、昨日(day -1)、一昨日(day -2)、翌一昨日(day -3)、明日(day +1)、明後日(day +2)、明々後日(day +3)など英語に翻訳できない、日本語特有の相対日指定の予約語を処理すべく、「日」ベースのカレンダー計算についてはやたらとAppleScriptで強力なルーチンを作りためてきました。

たまたま、「月」単位の相対計算を行う必要があった(ストック分に存在していなかった)ので、作ることになりました。たいがいの基礎ルーチンは作り置きがあるのですが、本当にこうして基礎ルーチンを作ったのは久しぶりです。

本当は固定で「現在月-2」の計算だけができればよかったのですが、±100か月ぐらいの範囲で動作確認したルーチンを作り置きしておきたいところです。気乗りしなかったので、ChatGPTに問い合わせてみたら、monthを秒で表現するタイプのルーチンを提案してきて、しかも内容が間違っていたので(構文確認を通過しないような内容を提案してくださります)、しぶしぶ作ってみました。ChatGPTが提案してきた作り方だと、おおよそ概算で500か月ぐらいの計算を行うと月単位の誤差が出てしまいます。

ChatGPTが「AppleScriptでrepeatループに0は指定できないよ?」とかの嘘を山のように出力してくるので、本当に初心者が使うと目が回るはずです(配列の要素が1から始まるのを「repeatで指定できない」という謎の過学習を発生させたもよう。ユーザーが間違ったフィードバックをやらかしている???)。

幾度かのアップデートを経て、完成しました。完成したといってよいのか? なんか無駄な処理が異様に多いような気もするのですが、結果が正しいのでよしとしましょう。

Cocoaの自然言語系の処理を用いて楽をして相対日付を処理しようかと試してみたものの、「本日」をtodayとしてピックアップしてしまうので、「本日から2か月前」といった認識ができませんでした。「現在のカレンダー」云々というのはCocoaの機能を使って処理しようとしていた頃のなごりです。

AppleScript名:相対年月を計算(月の相対指定)v4.scpt
use AppleScript
use scripting additions
use framework "Foundation"

repeat with i from 1 to 12
  set curDate to getDateInternational(2024, i, 1) of me
  
log curDate
  
set {yNum1, mNum1} to retYMbyRelativeMonth(curDate, -2) of me
  
log {yNum1, mNum1}
end repeat

on retYMbyRelativeMonth(curDate, relativeMonth)
  set yNum to (year of curDate) as number
  
set mNum to (month of curDate) as integer
  
set dNum to day of curDate
  
  
if relativeMonth = 0 then return {yNum, mNum}
  
  
set mDiff to (mNum + relativeMonth)
  
  
if mDiff ≤ 0 then
    –log {"case 1"}
    
set yDiv to (mDiff) div 12
    
set mNum to 12 + ((mDiff) mod 12)
    
set yNum to yNum + (yDiv) – 1
    
  else if mDiff > 12 then
    –log {"case 2"}
    
set yDiv to (mDiff – 1) div 12
    
set mNum to ((mDiff – 1) mod 12) + 1
    
set yNum to yNum + yDiv
    
  else
    –log {"case 3"}
    
set mNum to mDiff
  end if
  
  
return {yNum, mNum}
end retYMbyRelativeMonth

–現在のカレンダーで指定年月の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

★Click Here to Open This Script 

Posted in Calendar | Tagged 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

Skimで現在表示中のPDFで、現在表示中のページ以降を削除

Posted on 7月 2, 2024 by Takaaki Naganoya

Skimでオープン中のPDFに対して、現在表示中のページ以降をすべて削除するAppleScriptです。

もともとは、macOS 10.13以前のバージョンのmacOSで、BridgePlusを呼び出すようにしてあったものですが、スクリプトメニューからBridgePlusを呼び出せなくなって、BridgePlus呼び出し部分を別のサブルーチンに置き換えてみたものです。

# 一応、Script Debuggerから拡張アプレットで書き出せば、スクリプトメニューに入れたScript(アプレット?)からBridgePlusの呼び出し自体は行えます。

もともと、SkimにはPDFのページ削除の機能が存在していないので、情報取得後にPDFをいったんクローズ、AppleScriptだけでPDFKitの機能を呼び出して削除処理を行い、再度Skimでオープンするという処理を行なっています。

現在表示中のページから前を削除するScriptも書いて使っていますが、余計に処理に時間がかかります。

SkimのScript本を提案したこともあったのですが、開発チームのとくに誰が許可を出すというわけでもなく、そのまま時間が流れてしまいました。Skimの機能範囲だけだと実現できることに制限がありますが、AppleScriptからPDFKitの機能を呼び出すと、Skim自体で持っていない機能も実装できて便利です。

そういうものを目指していたのですが、いまひとつテンションが上がらずそのまま放置状態に。

AppleScript名:現在表示中のページ以降を削除.scptd
— Created 2018-06-30 by Takaaki Naganoya
— Modified 2024-07-01 by Takaaki Naganoya
— 2018–2024 Piyomaru Software
use AppleScript version "2.8"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "PDFKit"

property NSSet : a reference to current application’s NSSet
property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property PDFDocument : a reference to current application’s PDFDocument
property NSSortDescriptor : a reference to current application’s NSSortDescriptor

tell application "Skim"
  set docCount to count every document
  
if docCount = 0 then return
  
  
tell front document
    set curInd to index of current page
    
set docFile to file of it
    
close without saving
  end tell
end tell

set aPOSIX to POSIX path of docFile
set aURL to (|NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()

set pRes to removePDFPageAfter(docFile, curInd) of me

tell application "Skim"
  open docFile
  
tell front document
    go to last page
  end tell
end tell

–指定パスのPDFの指定ページ以降(指定ページも含む)をすべて削除
on removePDFPageAfter(inFile, sPage)
  set pCount to pdfPageCount(inFile) of me
  
set eCount to pCount – sPage + 1
  
  
set targPageList to makeSuccessfulNumArray(sPage, pCount) of me
  
set rList to reverse of targPageList
  
return removeSpecificPagesFromPDF(inFile, rList) of me
end removePDFPageAfter

–指定PDF書類の複数ページの一括削除
on removeSpecificPagesFromPDF(inFileAlias, targPageNumList as list)
  set inNSURL to |NSURL|’s fileURLWithPath:(POSIX path of inFileAlias)
  
set theDoc to PDFDocument’s alloc()’s initWithURL:inNSURL
  
  
–削除対象ページリストをユニーク化して降順ソート(後方から削除)
  
set pRes to theDoc’s pageCount()
  
set t3List to relativeToAbsNumList(targPageNumList, pRes) of me
  
  
repeat with i in t3List
    copy i to targPageNum
    (
theDoc’s removePageAtIndex:(targPageNum – 1))
  end repeat
  
  
–Overwrite Exsiting PDF
  
set aRes to (theDoc’s writeToURL:inNSURL) as boolean
  
  
return aRes
end removeSpecificPagesFromPDF

–絶対ページと相対ページが混在した削除対象ページリストを絶対ページに変換して重複削除して降順ソート
on relativeToAbsNumList(aList, aMax)
  set newList to {}
  
  
repeat with i in aList
    set j to contents of i
    
if i < 0 then
      set j to aMax + j
    end if
    
    
if (j ≤ aMax) and (j is not equal to 0) then
      set the end of newList to j
    end if
  end repeat
  
  
set t1List to uniquify1DList(newList, true) of me
  
set t2List to sort1DNumListWithOrder(t1List, false) of me
  
  
return t2List
end relativeToAbsNumList

–1D/2D Listをユニーク化
on uniquify1DList(theList as list, aBool as boolean)
  set aArray to NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
return bArray as list
end uniquify1DList

–Sort 1-Dimension List(String Number List)
on sort1DNumListWithOrder(theList as list, aBool as boolean)
  tell NSSet to set theSet to setWithArray_(theList)
  
tell NSSortDescriptor to set theDescriptor to sortDescriptorWithKey_ascending_("floatValue", aBool)
  
set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor}
  
return (sortedList) as list
end sort1DNumListWithOrder

–指定PDFのページ数をかぞえる
on pdfPageCount(aFile)
  set aFile to POSIX path of aFile
  
set theURL to |NSURL|’s fileURLWithPath:aFile
  
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:theURL
  
set aRes to aPDFdoc’s pageCount()
  
return aRes as integer
end pdfPageCount

–連番1D Listを作成
on makeSuccessfulNumArray(sNum, eNum)
  script spd
    property aList : {}
  end script
  
  
if sNum ≥ eNum then return {}
  
  
set (aList of spd) to {}
  
  
repeat with i from sNum to eNum
    set the end of (aList of spd) to i
  end repeat
  
  
return (aList of spd)
end makeSuccessfulNumArray

★Click Here to Open This Script 

Posted in PDF | Tagged 12.0savvy 13.0savvy 14.0savvy Skim | Leave a comment

開発機としてM2 Mac miniが来たのでガチレビュー

Posted on 7月 2, 2024 by Takaaki Naganoya

目下、AppleScriptで組んだ割と大きな規模(中規模ぐらい?)のシステムのアップデート開発依頼を受けて、開発機としてM2 Mac mini(RAM 8GB)が手元に来ています(開発機なので、終わったら返却しますが……)。

このため、M1 Mac mini(macOS 13.6.7)、M2 Mac mini(macOS 14.5)、そしてオリジナルのシステムが動いているIntel Mac mini(2014)(macOS 10.12.6)の3台が積み上がっている状態です。さして場所をとっていませんが……。

# 新規導入したMacBook Air M2 15インチ(RAM 16GB)の結果も追記しています

M2 Mac miniがどの程度M1 Mac miniからスピードアップしているのか、についてはこれまで比較する機会がありませんでした。店頭でちょっとさわって試すぐらいだと、単純ループ10億回とかの「実用性皆無」な内容でチェックするほかありませんでした。

いまのコンピュータは速くなっている箇所とそうでもない箇所の「差」が大きくなっていて、特定分野の処理だけやたら速くなっている(あるいはその逆も)という現象をよく見かけます。

そこで、いろんな分野のAppleScriptで実行速度を比較してみました。

■Pages書類の全書き出しとソート+連結

実際の、本気で業務をまるごとこなすようなAppleScriptのシステムを動かすと、どうなるんでしょうか?

たとえば指定フォルダ以下のPages書類をSpotlight検索ですべてピックアップして個別にPDF書き出しを行い、ファイル名順に連結するようなAppleScriptがあります。

ちょうど、いま作っている書籍「Pages+AppleScriptで本を作ろう!」が現段階で320ページなので、テスト用には十分な規模でしょう。

これを処理すると、

  M1 Mac mini(RAM 16GB):2分35秒
  M2 Mac mini(RAM 8GB):2分30秒

と、M1よりは若干速いものの、騒ぐほどではないぐらいの差でした。誤差範囲内といってよいでしょう。数回書き出しを行なって落ち着いた結果を採用しています(初回はどうもキャッシュせずに遅くなるようで)。

■8,000箇所の駅×700箇所の位置情報の距離計算とソート

日本全国の鉄道駅8,000箇所と全国700箇所(当時)のゲームセンター施設の距離計算を行い、「最寄駅が一番遠いゲームセンター」を計算するAppleScriptを試してみました。このぐらいの規模の処理なら、割と現実的なシステムといえるでしょう。

  M1 Mac mini(RAM 16GB):1分 31秒
  M2 Mac mini(RAM 8GB):1分 23秒
  M2 MacBook Air(RAM 16GB):1分 28秒

数回実行してみましたが、ほぼ同じ結果になりました。9%ぐらいM2が高速です。さきほどの結果よりも性能向上幅が大きくなっています。

■指定桁の順列組み合わせ計算(Permutation)

このあたりから、様子がおかしくなってきます。結果は結果として受け取らざるを得ないのですが、振れ幅がおかしい。9桁のデータの順列組み合わせ計算で、

  M1 Mac mini(RAM 16GB):62.7379秒
  M2 Mac mini(RAM 8GB):30.564秒
  M2 MacBook Air(RAM 16GB):44.905秒

M1→M2 Mac miniで約2倍。

■Numbersの選択中のセル(1000×5)への値の設定

GUIアプリであるNumbersの表の選択範囲(1,000行×5列)に対して、連続する値を書き込むテストです。Numbersは表からの値の取得が高速である反面、値の書き込みについては信じられないほど遅くなるという特性を持っています。この、Numbersの表データ設定はさまざまな「回避技」を生むことにもなっています。

ひとえに、このNumbersへの書き込み処理に時間がかかるためです。これを比較すると、

  M1 Mac mini(RAM 16GB):96.3056秒
  M2 Mac mini(RAM 8GB):35.1062秒
  M2 MacBook Air(RAM 16GB):43.649秒

おおよそ、M2が2.7倍速いという結果になりました。意味がわかりません。

一応、AppleScriptのバースト実行モード=アプリ側の応答を待たずに値設定を行う「ignoring application responses」指定で実行すると、

  M1 Mac mini(RAM 16GB):2.76秒
  M2 Mac mini(RAM 8GB):2.42秒
  M2 MacBook Air(RAM 16GB):0.77秒??????

と、処理時間の短縮が頭がおかしいレベル(通常は指定前の33%ぐらいの時間になる)であるものの、CPU間の差は10%程度と、納得できるレベルに落ち着きます。

ちなみに、Numbers側はAppleScriptが処理を終了したあとも処理を継続し、当初と同じぐらいの時間で処理を終えました。

■画像の空白検出

上位機種でトンでもなく(遅い)結果が出たりと、さまざまな場所で物議をかもしている、画像の空白検出処理。

処理速度を測る場合には、他のプログラムをなるべく起動しないとか、いろいろ条件を揃えてあげる必要があるわけですが、今回も波乱の結果を出すものでしょうか。慎重を期して、10回実行して平均値を取っています。単位は秒で、数値が小さく、バーが短いほうが高速です。

1×1の画像処理については、データが小さすぎて傾向をつかむのには適していないようですが、4Kや8Kの画像を扱うとM1より遅い処理を見かけます。

グラフにすると、

こんな感じで……M2のほうがわずかに遅い処理が散見されます。正直、このベンチマークは4Kとか8KとかフルHDの画像の1つの点だけ点灯しているものを検出しろというもので、実用性はあるものの、ベンチマークとしては特殊な内容といえるでしょう。

■総評

ときおり、目が覚めるような処理速度を叩き出すことがあると思えば、日常的に動かしている処理だと5〜10%程度の処理速度向上におさまっています。全体的には、ほぼこのような評価になることでしょう。

同じく16GBのRAMを搭載しているマシンであるとか、上位機種のM2 Proを搭載したマシンではどうなのかといった検証を行いたかったところです(手元にないのでテストできないのですが)。

ただ、決してM2 miniはM1より遅いということはありませんし、M1からの買い替えという話は同意できないものの、悪くないマシンだと思います。

Posted in Benchmark | Tagged 13.0savvy 14.0savvy | Leave a comment

メキシカンハットの描画

Posted on 6月 28, 2024 by Takaaki Naganoya

X(旧称Twitter)の一部で流行っている、むかーーしむかしのBASIC処理サンプルの1つ、メキシカンハット型グラフの描画AppleScriptです。

–> Download full script bundle with libraries

自分もChipmunk BASIC用に移植して投稿していたのですが、AppleScriptにフル書き換えするとどんなもんだと思い、書いてみました。

関数演算ライブラリは、自作のcalcLibASがあるので、これを使い、画像のダイアログ表示用ライブラリを使って画面表示。

AppleScriptでも1秒ぐらいで処理が終了してしまいますが、黒地に緑で描画すると、謎の「味」があります。

AppleScript名:メキシカンハット v2.scptd
— Created 2024-06-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

–パラメータから下地になる画像を作成する
set aSize to current application’s NSMakeSize(300, 300)
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)
set anImage to drawImageWithColorFill(anImage, {0, 0, 300, 300}, 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, 256 – v}, 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

★Click Here to Open This Script 

Posted in 3D dialog Image | Tagged 13.0savvy 14.0savvy 15.0savvy | Leave a comment

指定Bundle IDのアプリのsdefを指定フォルダに取り出す

Posted on 6月 27, 2024 by Takaaki Naganoya

新しいOSのベータ版や、古いOS環境のAppleScript用語辞書をまとめて取り出すために作成したAppleScriptです。

アプリケーションのバンドル内から直接sdefを取り出すと、他の要素を外部からincludeしているsdefでは「完全体」にならない(資料として価値がなくなる)ため、Script Editorで表示(レンダリング)させた状態のものをコピーするよう処理しています。

以前にもこうした種類のScriptを書いていたような気もするのですが、どこかに行ってしまいました。

macOSのバージョン、ビルドNo.などを取得し、コピーしたSDEFファイルに対象アプリのバージョン番号ともども記載するようにしています。

本Scriptはスクリプトエディタを操作するものであるため、Script Debuggerなどの他の実行プログラムから実行することが望ましいところです。

そして、macOS 15.0beta2でもScript Debuggerが起動しないため、肝心のmacOS 15Betaでsdef書き出しを自動化できていないという………

AppleScript名:指定Bundle IDのアプリのsdefを指定フォルダに取り出す.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2024/06/27
—
–  Copyright © 2024 Piyomaru Software, All Rights Reserved
—
use AppleScript
use scripting additions
use framework "Foundation"
use framework "AppKit"

set aList to {"com.apple.iCal", "com.apple.AddressBook", "com.apple.databaseevents", "com.apple.finder", "com.apple.imageevents", "com.apple.mail", "com.apple.MobileSMS", "com.apple.Music", "com.apple.Notes", "com.apple.Photos", "com.apple.QuickTimePlayerX", "com.apple.reminders", "com.apple.Safari", "com.apple.ScriptEditor2", "com.apple.speech.SpeechRecognitionServer", "com.apple.systemevents", "com.apple.Terminal", "com.apple.TextEdit", "com.apple.TV", "com.apple.systempreferences"}

set targFol to POSIX path of (choose folder with prompt "SDEF収集先フォルダを選択")

–Get OS Version and Build Number
set sDic to current application’s NSDictionary’s dictionaryWithContentsOfFile:"/System/Library/CoreServices/SystemVersion.plist"
set osV to (sDic’s objectForKey:"ProductVersion") as string
set bNo to (sDic’s objectForKey:"ProductBuildVersion") as string

–Loop by Bundle IDs
repeat with i in aList
  set j to contents of i
  
set aRes to retPathFromBundleID(j) of me
  
set appAlias to (POSIX file aRes) as alias
  
  
set vRes to gepAppVersionByID(j) of me
  
  
tell application "Script Editor"
    open appAlias –Open App –> SDEF will be displayed
    
    
tell front document
      set docPath to path of it
    end tell
  end tell
  
  
do shell script "sync" –Important!!!!
  
  
set aP1 to (current application’s NSString’s stringWithString:(docPath as string))
  
set theName to aP1’s lastPathComponent()
  
set aP2 to (current application’s NSString’s stringWithString:targFol)
  
set newPath to (aP2’s stringByAppendingPathComponent:theName)
  
set aExt to (newPath’s pathExtension()) as string
  
set newPath to (newPath’s stringByDeletingPathExtension() as string) & "_v" & vRes & "@" & osV & "(" & bNo & ")." & aExt
  
  
–Copy SDEF file
  
set aRes to (my copyFileAt:aP1 toFilePath:newPath)
  
  
–Close SDEF
  
tell application "Script Editor"
    tell front document
      close without saving
    end tell
  end tell
  
end repeat

–バンドルIDからApp Fileのパスを求める
on retPathFromBundleID(aBundleID)
  set aURL to current application’s NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
if aURL = missing value then return false –Error
  
return aURL’s |path|() as string
end retPathFromBundleID

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

–バンドルIDで指定したアプリケーションのバージョン番号を文字列で取得する
on gepAppVersionByID(aBundleID)
  
  
set aURL to current application’s NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
set theResult to {}
  
  
set theNSBundle to (current application’s NSBundle’s bundleWithURL:aURL)
  
  
if aURL is not missing value then
    set theVersionString to ((theNSBundle’s infoDictionary())’s objectForKey:"CFBundleShortVersionString")
    
    
if theVersionString is not missing value then
      return theVersionString as string
    end if
    
  end if
  
  
return ""
end gepAppVersionByID

★Click Here to Open This Script 

Posted in file sdef | Tagged 13.0savvy 14.0savvy 15.0savvy Script Editor | Leave a comment

AppleScriptによる並列処理

Posted on 6月 27, 2024 by Takaaki Naganoya

AppleScriptによる並列処理は、これまでにも何度かテストを行ってきました。

古くは、Mac OS X 10.5ぐらいの時代に行っていた、大量のEPS書類の破損チェック。これは、割と骨の折れる作業のうえに効果も大きかったので(実用性があって)よかったのですが、EPS書類を大量に扱う現場自体が一般的ではありません。また、macOS 13以降ではEPSを取り扱うAPI自体が廃止になり、実際に呼び出せなくなりました。

Intel Macの時代にノート機で並列処理(画像変換)を行ってみましたが、当時はSSDのI/O速度がボトルネックになって、並列化をすすめても変更前よりも速く処理することはできませんでした。

Intel Mac時代、たとえば4コア8スレッドのCPUでAppleScriptによる並列処理を行うと、4スレッド分動かすだけで割とCPUの処理が埋まるという状態になっていました。あとは、密度の高い処理を行うことで、CPUの熱問題に直面しやすくなったということもありました。外部機器により強制冷却といった話も必要になってきました(あるいは、デスクトップ機でやるとか)。

やがて、REST APIを呼び出す処理(とくに、高速メール送信など)といった、「並列処理すると効果が大きそうな用途」が見つかってきました。待ち時間が割と長いうえに、サーバー側の処理が多重化されているので、リクエストを大量に出せば大量に処理してもらえるという環境でもあります。

技術的には「十分に可能」な」レベルに基礎研究が進んできましたが、問題は「用途」です。

並列処理すると効果が大きくて、その速度的なメリットを多くのユーザーに共有できる「用途」。

この用途を見つけることが並列処理の大きなテーマになっていました。

最近「これならいけるのでは?」と考えている用途が、PDFのページごとの画像化処理です。

300ページ強のPDFを連番つきのJPEG画像に分割するのに、M1 Macでも1分ぐらいかかります。1ファイルあたり0.2秒以下で処理できているので十分に速いのですが、正直なところCPUの能力にはぜんぜん余裕がある状態ですし、1分も待たされるのはどうかと感じます。

これを並列処理化して、50パーセント程度の処理時間で完了できたら成功。これよりも短い時間で処理できたら大成功でしょう。

AppleScriptの並列処理については、macOSの並列処理用Frameworkを活用するのではなく、複数アプレットを同時起動し、それぞれのアプレットに指定のAppleScriptをローディング。中心となるAppleScriptから順次「手の空いている」アプレットに処理を割り振るという構造で動いています。

当初は、並列処理用アプレットをその場で生成して起動していたのですが、セキュリティ的にそのような運用が許可されなくなりそうだったので、「あらかじめ用意していたアプレットをCPUのコアの個数だけ起動」とかいった使い方をするように変化しました。

Posted in parallel processing | Tagged 13.0savvy 14.0savvy 15.0savvy | Leave a comment

GUI Scriptingに邪魔な各種パレットをクローズ

Posted on 6月 26, 2024 by Takaaki Naganoya

「操作自動化」の観点からするとメイン機能、「業務自動化」の観点からすると必要悪、操作対象のアプリのGUI部品の状況を想定どおりに設定しておけないと、Script動作の再現がなかなか大変なので、極力使わないでおきたいGUI Scripting。

PagesのAppleScript対応機能は必要な機能が呼び出せないのと、現在表示中のページ+5ページまでしか各種オブジェクトの属性値にアクセスできないので、書類の表示倍率を強制的に変更する必要があります。PagesのAppleScript用語辞書にそのような機能は実装されていないので、仕方なくGUI Scriptingで組むことに。

そんな中、各種パレットが表示されていると、パレットがwindow 1として認識されるため、作業前にパレットを消去しておく必要を感じました。

そこで、指定アプリのパレット表示状態を検知してクローズするAppleScriptを書いてみました。実行すると、

表示されている各パレットを、

順次クローズしていきます。

当初、もっと簡単にできるものとばかり思って、window 1(実際にはパレット)にcloseコマンドを実行したものの、それでは実行できず……地道にクローズボタンを探してclickするという処理内容になりました。

もっとお手軽に書いてしまってもよかったものの、日本語環境でだけ動くScriptというのも、それはそれでいまひとつなので(別に自分は日本語環境でだけ動けばよいのですが)、言語環境に依存しないように書いておきました。

よく使いそうな部品なので、ライブラリ化して呼び出すとよいでしょう。

AppleScript名:指定アプリのパレットウィンドウを閉じる.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2024/06/26
—
–  Copyright © 2024 Piyomaru Software, All Rights Reserved
—

set aRes to closeAllPallettes("Pages") of me

on closeAllPallettes(appName)
  activate application appName
  
delay 0.1 –this number depends of how fast the CPU (slower CPU require larger number. 0,1 for M1)
  
  
tell application "System Events"
    tell process appName
      if (count (every window)) = 0 then return
      
repeat
        tell window 1
          set aSubrole to subrole
          
if (aSubrole = "AXFloatingWindow") or (aSubrole = "AXSystemFloatingWindow") then
            try
              set bList to every button whose subrole = "AXCloseButton"
              
set aButton to first item of bList
              
tell aButton to click
            end try
          else
            exit repeat
          end if
        end tell
      end repeat
    end tell
  end tell
  
  
return true
end closeAllPallettes

★Click Here to Open This Script 

Posted in GUI Scripting | Tagged 13.0savvy 14.0savvy 15.0savvy Pages | Leave a comment

Pages本執筆中に、2つの書類モード切り替えに気がついた

Posted on 6月 18, 2024 by Takaaki Naganoya

目下、「Pages+AppleScriptで本をつくろう!」という電子書籍を執筆中です。

これまでの電子書籍作成のノウハウと、その中で必要に迫られて書いたAppleScriptのかずかずを盛り込んでいます。

AppleScriptについて詳しく説明を行わず、コンテンツの作成に比重を置いています(高機能なScriptは添付)。

そんな中、これまでに疑問に思っていたことについてPagesのヘルプでいろいろ調べてみたところ、いろいろ勘違いしていたことが分かりました。

Pagesは、ワープロ的に書類下地に本文を記入する「ワープロ的な世界観」と、ボックスを並べてテキスト流し込みを行う「DTP的な世界観」の2つの機能が混在していると思っていました。ただ、ワープロ的な世界観に無理やりにDTP的な世界観を統合したため、

・ページ単位の編集能力がない

と思っていました。しかし、これが自分の勘違いであることがわかりました。

数十ページも書き換える必要が出てきて頭を抱えているところです。

ものすごくわかりにくい場所に、モード切り替えスイッチが存在

これら2つの世界観に対して公式に名前が割り振られていることをヘルプで確認(ものすごくわかりにくい)。

ワープロ的世界観の書類のことを「文章作成書類」、DTP的な世界観の書類のことを「ページレイアウト書類」と呼んでいることを見つけました。画面上にそうした切り替え機能が(メニューなどに)、明確に用意されているものではありません。

Pagesで書類を新規作成したときに、作成されるのは「文章作成書類」(ワープロ的)です。

これまで、自分はこの「文章作成書類」の上にテキストボックスを並べて、DTPソフト的に使っていました。結果として、ページ単位の削除やならべかえが行いにくく、「ページ単位の編集ができないので、書類を小分けにして対処しよう」という作り方をしてきました。

ところが、このDTPソフト的な使い方に適した「ページレイアウト書類」モードに切り替えるスイッチが存在していたのです(こんなの気づかないぞ!)。

この、インスペクタを「書類」に設定し、「書類」タブを選択した中に存在している「書類本文」チェックボックス。デフォルトではこれが選択されています。

では、このチェックをはずすとどうなるか?

わざわざ「ページレイアウト書類に変換してもよろしいですか?」というダイアログを表示して警告します。

これが、「文章作成書類」モードと「ページレイアウト書類」モードの切り替えを行う操作です。最初から、ページレイアウト書類の「空白」テンプレートを用意しておいてほしいぐらいです(ユーザーによる追加は可能)。

Macのアプリでは「モーダルな処理をなるべく避けること」といったガイドラインが存在しているのですが、Pagesの場合はモーダルな機能を隠した結果、余計にわかりにくくなった印象です。

この2つの書類モードの切り替えは不可逆的なものではなく、随時切り替え操作が可能です。ただし、先のダイアログによる警告どおり、文章作成書類(ワープロモード)で本文テキストを入力していた場合には、それはすべて削除されます。

ページレイアウト書類に切り替えると機能も変わる

ページレイアウト書類モードに切り替えると、機能自体も変わります。

(1)ページ単位の編集が可能に

文章作成書類モードでは謎機能だった「セクション」が「ページ」と呼び替えられます。普通にページ挿入や、ページのサムネールを入れ替えるとページが入れ替えられたり、ページ単位の削除が行えます。

(2)オブジェクトの配置がなくなる

本文テキストとともにオブジェクトが移動したり、移動しなかったりという制御を行う「オブジェクトの配置」機能が、インスペクタ上から消えます。

「ページレイアウト書類」モードでは、本文というものがなくなるのに合わせて、インスペクタから消えます。

(3)ページ挿入ができるようになる

「セクションの挿入」が「ページの挿入」に切り替わります。

ページレイアウト書類モードに切り替えると、これまでPagesに感じてきた違和感がなくなります。

こんな重要な機能はもっとわかりやい場所に配置しておくべきです。

書類モードのAppleScriptからの検出

AppleScript名:書類モードの検出(文章作成書類、ページレイアウト書類).scpt
tell application "Pages"
  tell front document
    set dStat to document body
    
–> false –ページレイアウト書類
    
–> true –文章作成書類
  end tell
end tell

★Click Here to Open This Script 

AppleScriptからdocumentの属性値「document body」を取得するとtrueなら文章作成書類、falseならページレイアウト書類であることが分かります。

なお、この「document body」という属性値はr/o(Read Only)であるため、属性値の書き換えで切り替えることはできません。

Pagesの「文章作成書類」を前提とした末尾ページの削除や指定ページから末尾までの一括削除AppleScriptを作りためていましたが、これらは「ページレイアウト書類」モードでは機能しません。

ただ、ページレイアウト書類モードではページのサムネールを選択してページ削除が行えるので、これらのAppleScriptが機能しなくても問題はないでしょう。

Posted in Object control | Tagged 13.0savvy 14.0savvy Pages | Leave a comment

macOS 14.xでAppleScriptのバグ

Posted on 5月 25, 2024 by Takaaki Naganoya

Hi @Apple,

just a quick note on a serious #Sonoma #bug.

get " " as integer
(this line crashes)

Can someone please address this at the right spot?

(@applescripter, @applescriptguru, @applescript, @AppleScriptive, @Applescrip52206, @AppleS31305, @AppleScript12, @AppleScripter_)

— Torben Johannson (@BunterLotentony) May 24, 2024

空白文字をintegerにcastすると、処理系ごとクラッシュするというバグです。macOS 13.xではエラーになります(通常の動作)。

macOS 14.5ではクラッシュ(スクリプトエディタ上で動かせばスクリプトエディタがクラッシュ)します。

ほかにもいろいろあるんですけど(ーー;;;;;

macOS 15.0での修正

macOS 15では、下記のScriptの実行結果が数値の0を返すようになりました。

AppleScript名:sonomacrasher.scpt
get " " as integer

★Click Here to Open This Script 

Posted in Bug | Tagged 14.0savvy | Leave a comment

Keynote上で選択中のテキストアイテムを、位置情報をもとにテキスト連結してクリップボードへ

Posted on 5月 13, 2024 by Takaaki Naganoya

Keynoteで作業がめんどくさくなったら、その場でAppleScriptを書いて使うシリーズ。Keynote v14で動かしていますが、別にバージョン依存している箇所はありません。もっと古いKeynoteでも動くと思います(KeynoteのAppleScript対応機能的には、slideのselectionができるようになったv12が実用下限だと思っています)。

Keynote書類の、章トビラに内容を個別のテキストアイテムで箇条書きしていたような場合に、

これを、全部結合して1つのテキストアイテムに入れたくなる時があります。

そこで、本ScriptのようなものをmacOS標準搭載のスクリプトメニューに入れて、

呼び出すために作ったものです。

実行すると、それぞれのテキストアイテムをY座標に着目してソートを行い、上→下の順番にならべかえて、テキストを取り出し、改行をはさみつつ連結します。

この後で、もとのテキストアイテムにこの連結したテキストを入れることになります。

一番上(positionのY座標が一番小さい)のテキストアイテムだけ残してあとは削除するわけですが、このあたりのオブジェクト操作もAppleScriptから操作してしまったほうがよさそうです。

ただし、Keynoteはテキストアイテム内の文字の上寄せ/下寄せの制御をAppleScriptからできないので(キーボードショートかメニューでも操作する?)そのあたりに「残念感」が残ってしまうかもしれません。

macOS標準搭載のスクリプトメニューに入れるAppleScriptで、絵文字を大量に入れるのは、大量のAppleScriptをメニューに入れるため文字だけだと視認性が低く、色付き文字を入れてそれぞれを識別しやすくするためです。

AppleScript名:🧭位置情報🧭をもとに🈴テキスト連結🈴して📎📎クリップボードへ📎📎.scpt
use AppleScript
use scripting additions
use framework "Foundation"

set outList to {}
set outStr to ""

tell application "Keynote"
  tell front document
    set aSel to selection
    
    
if length of aSel = 0 then
      display dialog "Keynote書類上で何も選択されていません。" with title "オブジェクト選択エラー" buttons {"OK"} default button 1 with icon 2
      
return
    else if length of aSel = 1 then
      display dialog "Keynote書類上で複数のオブジェクトが選択されていません。" with title "オブジェクト選択エラー" buttons {"OK"} default button 1 with icon 2
      
return
    else if (class of first item of aSel = slide) then
      display dialog "Keynote書類上でスライドが選択されてしまっています。" with title "オブジェクト選択エラー" buttons {"OK"} default button 1 with icon 2
      
return
    end if
    
    
repeat with i in aSel
      set j to contents of i
      
set aClass to class of j
      
if aClass = text item then
        set {aPosX, aPosY} to position of j
        
set aCon to object text of j
        
set the end of outList to {xPos:aPosX, yPos:aPosY, textCon:aCon}
      end if
    end repeat
    
  end tell
end tell

set bList to sortListAscending(outList, "yPos") of me

repeat with i in bList
  set j to contents of i
  
set aText to textCon of j
  
set outStr to outStr & aText & return
end repeat

set the clipboard to outStr
beep 1

–入れ子のリストを昇順ソート(AppleScriptObjC)
on sortListAscending(theList as list, keyLabel)
  set anArray to current application’s NSMutableArray’s arrayWithArray:(theList)
  
set theDescriptor to current application’s NSSortDescriptor’s sortDescriptorWithKey:(keyLabel) ascending:(true)
  
set sortedList to anArray’s sortedArrayUsingDescriptors:{theDescriptor}
  
return sortedList as list
end sortListAscending

★Click Here to Open This Script 

Posted in list Object control Record Sort | Tagged 12.0savvy 13.0savvy 14.0savvy Keynote | Leave a comment

Keynoteで2階層のスライドのタイトルをまとめてテキスト化

Posted on 5月 10, 2024 by Takaaki Naganoya

Keynoteでページ数の多い書類、この場合は電子書籍「Cocoa Scripting Course」の9巻「File Processing」で章ごとの目次を作ろうとしたときに、各ページのタイトルを記事のレベルを反映しつつ文字列化するのが大変なので、AppleScriptで半自動化しました。

実際には、機械的に全スライドのタイトルを取得してテキスト化するAppleScriptを作って使っていたのですが、機械的にタイトルを取り出したあとで、レベルを反映させるといった「手作業」が発生していたので、半自動処理に変更したほうがまだ作業量が少なくなるという見立てになりました。

Keynote書類をオープンして、最終階層のスライドを畳んだ状態で処理範囲(同一レベルのスライド)を選択しておいた状態で本Scriptを実行します。もともと、章トビラに掲載する内容をテキストで取り出すためのものなので、1つの章を構成する中トビラのみを選択した状態にしてから実行する必要があります。

Keynoteでは、ウィンドウ左端にナビゲータを表示させ、各スライドのレベルを変更し、アウトラインプロセッサのように下位スライドの表示を隠したり、表示させたりできます。つまり、ページ数の多いスライドの整理を行いやすくなっています。

本来、このスライドのインデントレベルを数値で取得できる必要があります。それでも、KeynoteのAppleScript用語辞書にその機能は実装されていません。ただ、ないものにないと文句を言っているだけでは前に進めません。

Keynoteのウィンドウ上で下位スライドを非表示状態にして選択すると、selection中の第1階層のスライドのみ選択された状態になるため、スライド番号を取得すると、selection中の第2階層のスライド番号が「抜けた」状態になります。この違いによってスライドの階層を識別できます。

スライド番号を取得できたselection中の第1階層のスライドと、スライド番号を取得できなかったselection中の第2階層のスライドで別々に処理(インデント文字=tab)することで、文字列で階層の違いも表現できます。

出力例:

◽️再帰処理によるファイル検索  –第1階層
  再帰によるファイルパスの取得  –第2階層
  再帰によるファイルパスの取得+拡張子による抽出  –第2階層
  ファイル名衝突回避つきリネーム  –第2階層

処理結果はクリップボードに転送されます。つまり、他のテキストエディタに内容をそのままペーストできます。

スライドのインデントレベルを取得できないことへの対処は、スライドのbase layoutにインデント情報を書いておくとか、種別で判定するやりかたもあります。出現確率をそれぞれのbase layoutについて計算し、登場頻度の低いものをより上位の章トビラとして認識するといったAppleScriptもありますが、完全自動よりも決定前にユーザーに確認を取るぐらいの作りになっているほうが実用性が高い感じです。

AppleScript名:選択中のスライドから、2段階の階層のタイトルを取得してクリップボードへ.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2024/05/04
—
–  Copyright © 2024 Piyomaru Software, All Rights Reserved
—

use AppleScript
use framework "Foundation"
use scripting additions

property headerChar : "◽️" –1階層目のタイトルの行頭に入れる
property indentChar : tab –2階層目のタイトルの行頭に入れる

tell application "Keynote"
  tell front document
    set aSel to selection
    
set aFirst to class of first item of aSel
    
if aFirst is not equal to slide then
      display dialog "スライド単位で選択されていません(スライド内のオブジェクトが選ばれている)" buttons {"OK"} default button 1
      
return
    end if
    
    
–スライドの逆順選択が発生していた場合には、listを逆順に入れ替えて正順(開始ページ→終了ページ)に修正  
    
set fItem to slide number of first item of aSel
    
set lItem to slide number of last item of aSel
    
if fItem > lItem then set aSel to reverse of aSel
    
    
–スライド番号を選択部分から取り出す
    
set pNumList to {}
    
repeat with i in aSel
      set the end of pNumList to (slide number of i)
    end repeat
    
    
–選択中の最終スライドの次のスライドは説明部分とみなし、説明部分が続く(同じベースレイアウト)ブロックを検出
    
set totalCount to count every slide
    
set aMasterSlide to base layout of slide ((last item of pNumList) + 1)
    
set hitF to false
    
    
repeat with i from (last item of pNumList) + 2 to totalCount
      set tmpBase to base layout of slide i
      
if aMasterSlide is not equal to tmpBase then
        set hitF to true
        
exit repeat
      end if
    end repeat
    
    
if hitF = false then error "Page selection Error"
    
    
—末尾の情報を補う。そのため、本Scriptではスライド末尾までの選択が行えないが、実用上問題がないので無視している
    
copy i to lastThreadShould
    
copy pNumList to pNumList2
    
set the end of pNumList to i
    
    
    
–メインループ
    
set outList to {}
    
set aCount to 1
    
    
–1階層目のタイトル収集ループ
    
repeat with i in (pNumList2)
      
      
tell slide i
        try
          set tmpTitle1 to object text of default title item
        on error
          set tmpTitle1 to "" –タイトルが存在しないスライドなどの場合
        end try
      end tell
      
      
–オブジェクト内の強制改行文字を削除
      
set tmpTitle13 to repChar(tmpTitle1, string id 8232, "") of me
      
      
–出力用のリストに追加
      
set the end of outList to (return & headerChar & tmpTitle13)
      
      
–2階層目のタイトル収集ループ
      
repeat with ii from (i + 1) to ((item (aCount + 1) of pNumList) – 1)
        if ii ≥ (lastThreadShould) then exit repeat
        
        
tell slide ii
          try
            set tmpTitle2 to object text of default title item
          on error
            set tmpTitle2 to "" –タイトルが存在しないスライドなどの場合
          end try
        end tell
        
        
–オブジェクト内の強制改行文字を削除
        
set tmpTitle22 to repChar(tmpTitle2, string id 8232, "") of me
        
        
–出力用のリストに追加
        
set the end of outList to (indentChar & tmpTitle22)
      end repeat
      
      
set aCount to aCount + 1
      
    end repeat
    
    
–クリップボードに結果文字列を設定
    
set outStr to retListedText(outList, return) of me
    
set the clipboard to outStr
  end tell
end tell

–1D Listにデリミタ文字を入れつつテキスト化
on retListedText(aList, aSeparator)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aSeparator
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retListedText

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

★Click Here to Open This Script 

Posted in Object control | Tagged 13.0savvy 14.0savvy Keynote | Leave a comment

Keynoteでslideの逆順選択状態を解消する

Posted on 5月 9, 2024 by Takaaki Naganoya

Keynoteで日々さまざまな書類を作っており、Keynoteを使わない日はないと言って過言ではないほどです。

当然、Keynote関連の作業の効率化のためのAppleScriptを書くことが多くなります。そして、Keynote書類の連続したスライド(ページ)を選択し、それらのタイトルを加工するようなAppleScriptをよく使います。

たとえば、マル付き数字(例:①②③)の付け直し(リナンバー)AppleScriptはよく使いますが、

Keynote上で末尾ページから先頭ページに向かってページ選択できる、いわば「逆順選択状態」を画面上からの操作によって作り出せてしまうことに気づきました。

この状態でAppleScriptからselectionを取得すると、P-197、196、195、194、193、192、191と、意図しない順番でスライドオブジェクトを取得できてしまうことがわかりました。この逆順選択状態で、マル付き数字のリナンバーScriptを実行すると、末尾から番号が振られてしまいます。

この状態は、たとえば197ページを選択しておいて、SHIFTキーを押しながら、上向き矢印キーを押すことで選択範囲を拡張した場合に発生することがわかりました。この現象は、Keynote v14.0+macOS 13.6.7で発生を確認しています。

これは、バグではありません。ただ、意図しない状態を作り出せてしまうことについては、知っておく必要がある内容です。

selectionでアクセスできるSlideオブジェクトが、かならずしもページの先頭方向から順に末尾方向へと並んでいるわけではない、ということで対策が必要になりました。この手の、複数Slideを選択して実行するAppleScriptは割と多いので。

そこで、selctionから取得したSlideオブジェクトの先頭と末尾のSlide numberを取得し、先頭のSlide Numberが大きければ、selectionから取得したデータをその場で逆順に並べ替えるという対処を行いました。

AppleScript名:スライドの逆順選択状態を解消する.scpt
tell application "Keynote"
  tell front document
    set aaSel to selection
    
set fItem to class of first item of aaSel
    
if fItem is not equal to slide then
      display dialog "Slide内のオブジェクトが選択されています。Slide単位で選択してください。" buttons {"OK"} default button 1
      
return
    end if
    
    
set fItem to slide number of first item of aaSel
    
set lItem to slide number of last item of aaSel
    
    
–逆順選択が発生していた場合には、listを逆順に入れ替える
    
if fItem > lItem then
      set aaSel to reverse of aaSel
    end if
    
    
set fItem2 to slide number of first item of aaSel
    
set lItem2 to slide number of last item of aaSel
    
return {fItem2, lItem2}
  end tell
end tell

★Click Here to Open This Script 

Posted in Object control | Tagged 13.0savvy 14.0savvy Keynote | Leave a comment

Keynote/Pagesで選択中の表カラムの幅を均等割

Posted on 5月 5, 2024 by Takaaki Naganoya

Keynote/Pagesの書類上の「表」のうち、選択中のカラムについて「幅」を均等割する(等しい幅に設定する)AppleScriptです。Keynote/Pages v14.0で動作確認していますが、とくにバージョン固有の機能などに依存する部分はありません。


▲Keynote書類上の表のカラムを選択しておいてAppleScript実行


▲Keynote書類上で選択しておいた表カラムが均等幅に設定される


▲Pages書類上の表のカラムを選択しておいてAppleScript実行


▲Pages書類上で選択しておいた表カラムが均等幅に設定される

AppleScript名:選択中の表カラムの幅を均等割.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2024/05/05
—
–  Copyright © 2024 Piyomaru Software, All Rights Reserved
—

tell application "Keynote"
  tell front document
    set aSel to selection
    
set fObj to class of first item of aSel
    
if fObj is not equal to table then
      display notification "tableが選択されていません"
      
return
    end if
    
    
tell current slide
      try
        set theTable to first table whose class of selection range is range
      on error
        display notification "table中のセル(カラム)が選択されていません"
        
return –何も選択されてなかった場合
      end try
      
      
tell theTable
        
        
set vList to address of column of every cell of selection range
        
set vUList to uniquify1DList(vList) of uniquifyKit of me
        
        
–均等割したカラム幅を計算
        
set totalW to 0
        
repeat with i in vUList
          tell column i
            set tmpW to width
            
set totalW to totalW + tmpW
          end tell
        end repeat
        
        
–選択カラム幅に均等割した幅を設定
        
set aColW to totalW / (length of vUList)
        
repeat with i in vUList
          tell column i
            set width to aColW
          end tell
        end repeat
        
      end tell
    end tell
    
  end tell
end tell

script uniquifyKit
  use scripting additions
  
use framework "Foundation"
  
property parent : AppleScript
  
  
on uniquify1DList(theList as list)
    set theSet to current application’s NSOrderedSet’s orderedSetWithArray:theList
    
return (theSet’s array()) as list
  end uniquify1DList
end script

★Click Here to Open This Script 

AppleScript名:選択中のカラムの幅を均等割.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2024/05/05
—
–  Copyright © 2024 Piyomaru Software, All Rights Reserved
—

tell application "Pages"
  tell front document
    set aSel to selection
    
set fObj to class of first item of aSel
    
if fObj is not equal to table then
      display notification "tableが選択されていません"
      
return
    end if
    
    
try
      set theTable to first table whose class of selection range is range
    on error
      display notification "table中のセル(カラム)が選択されていません"
      
return –何も選択されてなかった場合
    end try
    
    
tell theTable
      
      
set vList to address of column of every cell of selection range
      
set vUList to uniquify1DList(vList) of uniquifyKit of me
      
      
–均等割したカラム幅を計算
      
set totalW to 0
      
repeat with i in vUList
        tell column i
          set tmpW to width
          
set totalW to totalW + tmpW
        end tell
      end repeat
      
      
–選択カラム幅に均等割した幅を設定
      
set aColW to totalW / (length of vUList)
      
repeat with i in vUList
        tell column i
          set width to aColW
        end tell
      end repeat
      
    end tell
    
  end tell
end tell

script uniquifyKit
  use scripting additions
  
use framework "Foundation"
  
property parent : AppleScript
  
  
on uniquify1DList(theList as list)
    set theSet to current application’s NSOrderedSet’s orderedSetWithArray:theList
    
return (theSet’s array()) as list
  end uniquify1DList
end script

★Click Here to Open This Script 

Posted in Object control | Tagged 12.0savvy 13.0savvy 14.0savvy Keynote Pages | Leave a comment

指定のWordファイルをPDFに書き出す

Posted on 5月 4, 2024 by Takaaki Naganoya

ほとんど書き捨てレベルのAppleScriptですが、Microsoft Office用のAppleScriptの動作状況(ランタイム環境を変更すると動かなくなるなど)に興味があったので、書いてみました。

初回実行時にWord書類が入っているフォルダへのアクセス許可を求めるダイアログが出ますが、2回目以降はとくに聞かれません。

また、PowerPoint用のAppleScriptで、スクリプトメニューから実行すると実行がブロックされるという「Microsoft、正気か?」という仕様になっていましたが、いまMicrosoft Word用の本AppleScriptをスクリプトメニューに入れて呼び出しても実行できることを確認しています(macOS 13.6.7+Word バージョン 16.84 (24041420))。

AppleScript名:指定のWordファイルをPDFに書き出す.scpt
set aFile to choose file
automatorRun({aFile}, missing value) of me

on automatorRun(input, parameters)
  repeat with i in input
    set ipath to i as text
    
    
tell application "System Events"
      set o to name of i
    end tell
    
    
set o to repChars(ipath, ".docx", ".pdf") of me
    
    
tell application "Microsoft Word"
      close every document without saving
      
open i
      
save as document 1 file name o file format (format PDF)
      
close every document without saving
    end tell
  end repeat
  
  
return ""
end automatorRun

on repChars(origText as string, targStr as string, repStr as string)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChars

★Click Here to Open This Script 

本Scriptを書くことになったreddit上の投稿への回答となるScriptはこちら。

AppleScript名:指定のWordファイルをPDFに書き出す(For Automator Action).scpt

on run {input, parameters}
  repeat with i in input
    set ipath to i as text
    
    
tell application "System Events"
      set o to name of i
    end tell
    
    
set o to repChars(ipath, ".docx", ".pdf") of me
    
    
tell application "Microsoft Word"
      close every document without saving
      
open i
      
save as document 1 file name o file format (format PDF)
      
close every document without saving
    end tell
  end repeat
  
  
return ""
end run

on repChars(origText as string, targStr as string, repStr as string)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChars

★Click Here to Open This Script 

Posted in Object control PDF | Tagged 12.0savvy 13.0savvy 14.0savvy Word | Leave a comment

macOS 14.xでScript Menuの実行速度が大幅に下がるバグ

Posted on 4月 27, 2024 by Takaaki Naganoya

Late Nigt Softwareのフォーラムで、macOS 14.4.1上のスクリプトメニューから連絡先.app(住所録)を操作するAppleScriptを動かしたら、Script Debugger上の実行速度の27倍も時間がかかったという報告がありました。

基礎的なテストを行うために、コンスタントに時間がかかる処理でおなじみのPermutation(1次元配列の順列組み合わせ計算)をためしてみたところ、外部のアプリを操作しない処理内容にもかかわらず、この段階で8倍程度の時間がかかりました。外部アプリの操作を行うと、より一層時間がかかるもようです。

これは、macOS 11上でAppleScriptをM1のEコアで実行して、大幅にAppleScriptの実行速度が低下したバグを想起させるものです。

また、スクリプトメニューはAppleScript環境における「最終防衛ライン」です。このライン内にAppleによる妨害が行われると環境全体の有用性が大幅に下がってしまいます(もうそろそろ、Appleをあてにせずに自分でスクリプトメニューを作ることも考えてもいいのかも?)。

SED=スクリプトエディタ、MEN=スクリプトメニュー

6 digits
SED: 0.099387049675 seconds
MEN: 0.515756964684 seconds

7 digits
SED: 0.501626014709 seconds
MEN: 3.565899968147 seconds

8 digits
SED: 33.727347016335 seconds
MEN: 263.738371968269 seconds

これが、バグなのかAppleのエンジニアによる嫌がらせなのか、セキュリティ向上の美名のもとに行われている破壊活動なのかは不明ですが、こんなに処理速度を落とすことに正当な意味があるとは思えません。

また、「おまえんとこの最新鋭機種、10年前のへっぽこ最底辺マシンの10倍以上遅い処理があるぞ」とかバグレポートに書かれないと直さないんでしょうか? バグレポートに書く罵倒文句にもいい加減、在庫が尽きてしまいそうなのですが、、、

本件、AppleScriptランタイムに「osascript」を用いているものは、一律に遅くなっている可能性があるので注意が必要です、

Posted in Bug | Tagged 14.0savvy | Leave a comment

Post navigation

  • Older posts
  • Newer posts

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

Google Search

Popular posts

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

Tags

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

カテゴリー

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

アーカイブ

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

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

メタ情報

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

Forum Posts

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

メタ情報

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