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

AppleScriptでMyriad Tables Libが使えないケース

Posted on 7月 28, 2024 by Takaaki Naganoya

Myriad Tables Lib(最新版はv1.13)はShane StanleyによるAppleScriptライブラリで、かつてのClassic MacOSに存在していたGUI記述系のOSAX(名前は忘れた)を思わせるほど柔軟性が高いAppleScriptライブラリの逸品です。

自分としては、これを用いる場合でも過度な作り込みを行わず、編集可能なデータビューワー的な機能を手軽に追加するのがよいと感じています。

自分でもさらに機能を簡略化した「display table by list」ライブラリを用意しています。

macOS自体のセキュリティ制限により使えないケースも

ところが、本ライブラリが機能しないケースが確認されました。

特定の呼び方を行うと、ダイアログ+表の表示そのものが封じられるという、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に聞いてみた

その後、Shaneに実際に聞いてみたところ、ダイアログ表示をメインスレッド実行しないとできないらしい(意訳)ことが明らかになってきました。

ダメ元で表UIつきダイアログ表示を明示的にメインスレッド指定して実行してみたところ、ハネられました。

自作の「display table by list」ライブラリを同様に呼び出してみたところ、これも表示できませんでした。このライブラリは自分が作ったのでソースコードをすべて自分が管理しており、アラートダイアログの表示ハンドラをメインスレッド指定で呼び出しています。どの時点でメインスレッド指定実行しないとダメなのかは明確ではありませんが、割と八方ふさがりな状況です。

Posted in GUI Script Libraries Security | Tagged 13.0savvy 14.0savvy | Leave a comment

メキシカンハット v3a

Posted on 7月 28, 2024 by Takaaki Naganoya

メキシカンハットの描画AppleScriptのedama2さんバージョンで、さらに多色対応したものです。

–> Download mexicanHatv3a

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

★Click Here to Open This Script 

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

メキシカンハット v3

Posted on 7月 28, 2024 by Takaaki Naganoya

メキシカンハットの描画AppleScriptの、edama2さんバージョンです。

–> Download mexicanHatv3

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

★Click Here to Open This Script 

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

メキシカンハット v2a

Posted on 7月 28, 2024 by Takaaki Naganoya

以前掲載したメシキカンハット描画AppleScriptが、Retinaディスプレイ環境で表示が崩れていたため、修正してみました。

実行は、以下のScript Bundleをダウンロードして行なってください。

–> Download maxicanv2a.scptd


▲非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

★Click Here to Open This Script 

Posted in Number | Leave a comment

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

Developer CD November 1993

Posted on 7月 6, 2024 by Takaaki Naganoya

たまたま、当時の職場にあったCDを入手して(いらなかったのをもらってきた)、長年机のコヤシになっていたものを再発掘。

このCD自体はHFSフォーマットだったので、macOS 10.7搭載のオリジナルMacBook Airでマウントして中身を見ました。

New System Software Extensionフォルダに、AppleScriptが入っています。この数年前のCDから見ていましたが、この時点で初登場というわけではなく、2・3年前からずっと収録されていました。

目下、当時のSystem 7.xの環境は手元にないので(エミュレータぐらい?)これをインストールすることはできませんが、いろいろ入っていることがわかります。

このScripting Additionsフォルダにコマンドが入っています。当時はコマンド1つに1つのScripting Additionsが存在していたようなので、いまでは標準搭載のコマンドが別になっていました。

これだけでどうやってScriptを書いていいのかわからないぐらい素朴な命令群。Hypercardで書いたプログラムをScripting Additionsに変換できたようなので、割とHypercard由来のScripting Additionsが存在していました。

Posted in History | 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 15, Sequoia

Posted on 6月 14, 2024 by Takaaki Naganoya

WWDC24において、macOS 15, Sequoia(セコイア)が発表されました。

このところ、初期β版ではインストール最中に止まったまま動かなくなるなど「ひどい出来」でしたが、macOS 15ではそういうことはありませんでした。

AppleScriptのバージョンは引き続きv2.8。これは予想どおりです。次のmacOS 16でv2.9になるのか、そのままなのか……そこが注目点でしょう。macOS 15には「安定動作」を願いたいところです。

ひととおり、AppleScript対応のデフォルト搭載アプリケーションの用語辞書を調べてみたところ、差分が検出されたのはSafariのtabにpid(プロセスID)というプロパティが新設されたぐらい。個別にプロセスをkillするための仕掛けでしょうか。

このところ、不具合が出まくっていたText To Speechまわりで、大きな変更がありました。いろいろ読み上げ音声キャラクタが追加されています。ただし、AppleScriptのsayコマンドで指定可能な日本語読み上げキャラクタは、従来どおりKyoko、Otoyaぐらい。音声キャラクタ名がローカライズ表示されてしまうものについては、sayコマンドで指定できません。

macOS 15環境のAppleScriptは、いまのところ「macOS 14と同じ」ように感じられます。

つまり、macOS 14で知られているバグはmacOS 15でも同様に存在します。何も手が加わっていません。

サードパーティのアプリケーションが起動できないといった問題が存在していますが、これは初期βならではということでしょうか。

ちなみに、ウィンドウの吸着機能については、その日のうちにオフにしました。

システム設定>デスクトップとDock>ウィンドウに設定があるので、これらをすべてオフにするといい感じです。おそらく、自分は二度とこの機能をアクティブにすることはないでしょう。

macOS 15 AppleScript修正/バグまとめ

◯[追加] Safariのtabにpidプロパティが追加される
◯[修正] AppleScriptドロップレットにドロップしたファイル/フォルダが、xattr「com.apple.quarantine」の影響とGatekeeperの兼ね合いですべて受け渡されない現象がmcOS 10.12から継続して発生していたが、これが改められ、ドロップされたファイル/フォルダの欠損が発生しなくなった。なお、この変更がmacOS 13.xおよび14.xにもさかのぼって適用された。
◯[修正] 半角スペース文字列の数値への型変換で処理系自体がクラッシュするバグ(14.0, Sonoma Crasher)が解消

×[バグ] スクリプトエディタ上で「Cocoa-AppleScript Applet」のテンプレートから作成したアプレットが動作しない問題が継続中

×[バグ] スクリプトメニューから呼び出したAppleScriptの実行速度が、スクリプトエディタ上よりも6倍以上遅くなる現象が継続中
×[バグ] テキスト読み上げキャラクタのうち一部のキャラクタで名前がローカライズされてしまい、sayコマンドで指定できないバグが継続中(日本語環境限定バグ)

Posted in news | Tagged 15.0savvy | 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

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を採用
  • AppleScriptによる並列処理
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • Cocoa Scripting Course 続刊計画
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • Keynote/Pagesで選択中の表カラムの幅を均等割
  • macOS 15でも変化したText to Speech環境
  • デフォルトインストールされたフォント名を取得するAppleScript
  • macOS 15 リモートApple Eventsにバグ?
  • AppleScript入門① AppleScriptってなんだろう?
  • macOS 14で変更になったOSバージョン取得APIの返り値
  • Keynoteで2階層のスライドのタイトルをまとめてテキスト化

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 (132) CotEditor (66) Finder (51) iTunes (19) Keynote (117) 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年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

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

メタ情報

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

Forum Posts

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

メタ情報

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