先日のAppleEventでmacOS 14の9月27日に正式リリースという話が発表されました。
…..本気?(^ー^;;
先日のAppleEventでmacOS 14の9月27日に正式リリースという話が発表されました。
…..本気?(^ー^;;
Finder上で選択中のPDFのページ数を合計してダイアログ表示するAppleScriptです。ずいぶん前に作っておいたScriptのアップデート版です。
以前に作っておいたものは、BridgePlus Script Libraryを必要としていたために、ランタイム環境がScript DebuggerもしくはSDから書き出した拡張アプレットに限られていました。
これはこれで動作して便利なのですが、macOSが提供しているさまざまなランタイム環境およびサードパーティのソフトウェアが提供するランタイム環境では、このままでは動作しませんでした。BridgePlus依存部分を別のScriptに置き換えることで、より広範なランタイム環境で動作するようになるわけです。
今回置き換えたのは、指定パスのファイルからUTIを求める処理。割と、さまざまな機能を用いた部品を用意してあったので、入れ替えるだけで済みました。
本Scriptでは、Finderで選択中のファイルのUTIをすべて求め、指定のUTI(com.adobe.pdf)に該当するかどうかをチェックしています。今回のような処理では、拡張子のチェックだけで済みそうな話ですが、「画像全般」(public.image)を指定しておけば、PNGだろうがJPEGだろうが画像ならなんでもピックアップできるので、そうした処理のために用意しておいたものです。
本ScriptはPDFのページカウントなのでPDFKitの機能を利用していますが、メタデータにページ数が書いてある書類であれば、メタデータ経由で取得してもよいでしょう。最終的には、実際に書類をオープンして数えてもいいわけで。
▲Finderで選択していたPDFのページ数合計を表示。コピペできるようにしてある
合計ページ数を計算したかったので素朴な表示を行なっていますが、表インタフェースでそれぞれのPDFのファイル名とページ数を一緒に表示してもいいかもしれません。
AppleScript名:Finder上で選択中のPDFのページ数を加算.scptd |
— Created 2018-06-26 by Takaaki Naganoya — Modified 2023-09-13 by Takaaki Naganoya — 2018-2023 Piyomaru Software use AppleScript version "2.8" use scripting additions use framework "Foundation" use framework "PDFKit" –macOS 12 or later 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 NSPredicate : a reference to current application’s NSPredicate property PDFDocument : a reference to current application’s PDFDocument property NSSortDescriptor : a reference to current application’s NSSortDescriptor property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch –set inFiles to (choose file of type {"pdf"} with prompt "Choose your PDF files:" with multiple selections allowed) tell application "Finder" set inFiles to selection as alias list end tell if inFiles = {} then display dialog "No PDF Selection on Finder…" with icon 1 return end if –指定のAlias listのうちPDFのみ抽出 set filRes1 to filterAliasListByUTI(inFiles, "com.adobe.pdf") of me set totalP to 0 repeat with i in filRes1 set tmpCount to pdfPageCount(contents of i) of me if tmpCount > 0 then set totalP to totalP + tmpCount end if end repeat display dialog "Total Pages:" default answer (totalP as string) with icon 1 –指定PDFのページ数をかぞえる(10.9対応。普通にPDFpageから取得) –返り値:PDFファイルのページ数(整数値) on pdfPageCount(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 –Alias listから指定UTIに含まれるものをPOSIX pathのリストで返す on filterAliasListByUTI(aList, targUTI) set newList to {} repeat with i in aList set j to POSIX path of i set tmpUTI to my retUTIfromPath(j) set utiRes to my filterUTIList({tmpUTI}, targUTI) if utiRes is not equal to {} then set the end of newList to j end if end repeat return newList end filterAliasListByUTI –指定のPOSIX pathのファイルのUTIを求める on retUTIfromPath(aPOSIXPath) set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value) if theResult = true then return theValue as string else return theResult end if end retUTIfromPath –UTIリストが指定UTIに含まれているかどうか演算を行う on filterUTIList(aUTIList, aUTIstr) set anArray to NSArray’s arrayWithArray:aUTIList set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr) set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list return bRes end filterUTIList –リストに入れたレコードを、指定の属性ラベルの値でソート on sortRecListByLabel(aRecList as list, aLabelStr as string, ascendF as boolean) set aArray to NSArray’s arrayWithArray:aRecList set sortDesc to NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF –set sortDescArray to current application’s NSArray’s arrayWithObjects:sortDesc–macOS 11でクラッシュ set sortedArray to aArray’s sortedArrayUsingDescriptors:{sortDesc} return sortedArray end sortRecListByLabel |
外付けSSDに別バージョンのOSをインストールしておいて、β版の評価時には外付けSSDで起動する……という運用がmacOS 12あたりからできなくなってきています。そのため、業務用のMacとβテスティング用のMacを別に用意できず、macOS 14, Sonomaのβテストについては「完全無視」という今日このごろです。
書籍などで、β版OSでスクリーンショットを撮るわけにもいきませんので、当然な状況です。また、β版で気合いを入れてチェックしても、Release版でβ版以下のクオリティのOS(バグだらけ)になってしまうことが続いており、βテストの意義そのものを疑っています。
そんな中で、Sonomaのβテスターから漏れ伝わってくる「SonomaのCocoa Scriptingが遅い」という噂。
すべてのテスターから詳細な情報を聞けていないものの、何か特定の機能で10倍ぐらい速度が遅くなっている領域があるようです。よくある、Permutation(いくつかの要素の全パターンの組み合わせを計算出力)における速度低下とかいう「処理データ数がふえると遅くなる」という話ではなく、どうも遅くなる箇所が偏在しているようです。
いわく、
–InDesignの組版でCocoa Scriptingを利用しており、10倍ぐらい遅くなった
–GamePlayKit.frameworkの機能を呼び出して配列のシャッフルを行ったら10倍ぐらい遅くなった
などなど。
テスターが自分のマシンが何なのかを書かないので、どの程度深刻なのかが見えてきませんが、OSをアップデートしたら最新のマシン(の特定の処理)が10分の1の速度になったら嫌ですよね?
OS自体の機能はたいして機能アップしないのに、バグとか不具合ばかりが増えるというのは残念なことです。
macOS 13.x上でも日本語入力の速度の極端な低下が見られたりと、SoCの高パフォーマンス・コアと省エネ・コアの振り分けに失敗している疑惑があるものの、macOS 14βの問題がこれによって発生しているのかどうかは不明です。
macOS 10.15の際にもCocoa Scriptingの速度低下が見られ、macOS 12まで修正されませんでした(macOS 11で自分がレポートして修正)。
毎度毎度、目がさめるような不具合やトラブルを起こして、Release版でさらに巨大な不具合を作り込むとは、もはや職人芸としか言いようがありません。某社の組織の運営とか管理方法に大きな問題があるとしか思えません。
HammerspoonでLuaのコードを実行するAppleScriptです。
Hammerspoonは、ちょっと長々とした説明が必要なツールです。Mac用の自動化ツールの一種で、キー入力やメニュー操作というGUI Scriptingの範囲の自動化が行えるものです。
特徴的なのは、プログラミング言語Luaで設定および動作内容を記述できるようになっていることで、Luaの言語仕様が小さいため、「ああ、こういうふうに書くのか」と眺めているだけで書けることでしょう。
そして、このHammerspoon内蔵のライブラリのAPIにはAppleScriptを呼び出すものがあり、とりあえずワンライナーのScriptの呼び出しはうまく行きました(hs.applescript)。
Script名を指定して実行するAPI(hs.osascript.applescriptFromFile)についてはまだ動かせていません。
→ できました。API名の記述を間違っていたようです
そして、オマケ機能でHammerspoon側にもAppleScript用語辞書が付いており、
設定ファイル「init.lua」に「hs.allowAppleScript(true)」と記述しておくと、Hammerspoon.appでAppleScriptからLuaのプログラム実行を行えるようになります。
AppleScript名:HammerspoonでLuaを実行.scpt |
tell application "Hammerspoon" –Hammerspoon上でLua Codeを実行するためには、init.lua側で –hs.allowAppleScript(true) –を書いておく必要があります execute lua code "hs.alert.show(’display Result’)" –> "877B1BFD-07B6-4009-8B89-CC0487E2680C" end tell |
実行すると、Hammerspoon側のダイアログ、
が表示されます。
GUI Scriptingの範囲でしかアプリケーション操作ができないのでは、ほとんど意味がないと感じるものです(AppleScriptを直接起動できて制約の少ないランチャーが他にいろいろあるので)が、どうもキーボード操作だけですべての操作を行いたい一部のユーザーにはそこそこの支持を受けているようです。
本記事は、「AppleScript最新リファレンス v2.8対応」の調査の中で出てきたものを掲載してみたものです。AppleScriptランタイム環境のうちの1つとして挙動を調べてみましたが、単なるosascriptかと思いきや、「Hammerspoon」という名前を返してくる、かなり癖の強い環境でした。
最近、OS側のセキュリティ機能が強化され、さまざまな「制約」がデフォルト状態で設けられています。これまでと同様に自由にアプリケーションやOS機能のコントロールを行うためには、どのAppleScriptランタイムを用いてAppleScriptを実行し、そしてランタイム環境ごとにどのような機能差が存在しているかを知る必要があります。
「AppleScript最新リファレンス v2.8対応」は、それらの差を明確化して掲載し、Aという環境で動くからBという環境でも問題なく動くと思った。でも実際には動かなかった! このOSは壊れている! というよく聞く「勘違い」への処方箋として活用していただけることでしょう。
Piymaru Softwareによる電子書籍の84冊目、「ゆっくりAppleScript解説(3)」を発売しました。PDF42ページで構成されています。
第7章:デフォルト色のまま使うのはヤバいんだぜ!
第8章:tellを閉じるのに「end tell」と書くのは無駄無駄無駄無駄無駄ァ! なんだぜ!
第9章:「遅い」という文句は、残念なヤツの自己紹介なんだぜ
AVSpeechSynthesizerを呼び出して、指定文字列を読み上げる(Text to Speech)AppleScriptです。
AppleScript標準装備のsayコマンドと比べて、声の高さや読み上げ速度の設定範囲が広いようで、未知の読み上げ音声が聞こえます。
Siriの音声キャラクタはまだ指定できないようですが、次のOSぐらいでできたりするものでしょうか?
音声レンダリングした内容をファイルに書き込む方法が分かれば、さらにいろいろできそうです。
AppleScript名:Voice Character IDと音程、速度、音量を指定して読み上げ.scptd |
— – Created by: Takaaki Naganoya – Created on: 2023/08/28 — – Copyright © 2023 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use framework "AVFoundation" use scripting additions set aSynth to current application’s AVSpeechSynthesizer’s alloc()’s init() set aUtte to current application’s AVSpeechUtterance’s alloc()’s initWithString:"こんにちは。私の名前はおとやです。" aUtte’s setVoice:(current application’s AVSpeechSynthesisVoice’s voiceWithIdentifier:"com.apple.voice.enhanced.ja-JP.Otoya") –aUtte’s setVoice:(current application’s AVSpeechSynthesisVoice’s voiceWithIdentifier:"com.apple.voice.compact.ja-JP.Otoya") ( aUtte’s setRate:(0.6 as real)) –0.0から1.0。デフォルト 0.5 【速度】(aUtte’s setPitchMultiplier:(1.8 as real)) –0.5から2.0。デフォルト1.0 【音程】 (aUtte’s setVolume:(1.0 as real)) –0.0から1.0。デフォルト1.0 【音量】 ( aSynth’s speakUtterance:(aUtte)) |
Piymaru Softwareによる電子書籍の83冊目、「Cocoa Scripting Course #6, PDFKit」を発売しました。PDF560ページ+サンプルScript Zipアーカイブで構成されています。
PDF処理は、Cocoa Scriptingの1つの価値ある到達点です。この処理が行いたいからCocoaの呼び出しについて苦労を重ねてきたと言えます。機械学習やREST API呼び出し、配列からの高速なデータ抽出など、Cocoa Scriptingの「おいしい用途」は星の数ほどありますが、PDF処理は間違いなくその中でもトップランクの攻略目標のうちの1つです。
日常的にPDFを扱っているScripterなら、Cocoa Scriptingによって得られるメリットが膨大なものであることをもれなく間違いなくいかんなく実感できることでしょう。
1章 入門編
2章 実践編
3章 PDFKit編
PDFKitの位置付けと役割
用途別のフレームワークを知ろう
PDFKit内の主要クラス
PDFKit+AppleScriptのつかいどころ
PDFKit+AppleScriptの注意点
PDFKit.frameworkの利用宣言文
(参考資料)PDFの座標系
PDFDocument Basic Samples
PDFPage Basic Samples
PDFOutline Basic Samples
PDFAnnotation Basic Samples
4章 PDF処理 基礎編
PDFの処理の流れ
AppleScript+PDFKitでよく使う基礎的な処理
AppleScript+PDFKitの基礎的な処理手順
AppleScript+PDFKitでよく使う基礎的な処理の手順
PDFからの情報取得
PDFのサイズをPointで取得
PDFから各種情報をNSDictionaryで取得
PDFから各種情報を文字列で取得
PDFページカウント
PDFのページカウント(PDFDocument)
PDFのページカウント(Metadata Lib経由)
PDF本文テキスト抽出
PDFの全ページのテキストを抽出
PDFのテキストをページ単位で抽出
PDFを回転
PDFを回転させて新規保存
PDFを印刷
PDFを印刷
RTFをPDFに変換
RTFをPDFに変換
PDFからのテキスト検索
PDFテキストからの指定キーワード検索
ページ単位でPDF分割
ページ単位でPDF分割
複数PDF結合
choose fileコマンドで選択した複数のPDFを結合
PDF→他形式画像変換
ページ単位でJPEG画像に変換
画像連結してPDF作成
フォルダ内のJPEG画像を新規PDFに連結
指定フォルダ下のすべての画像を新規PDFに連結
フォルダ内のJPEG画像を既存のPDFに連結
Multi Page TIFFからPDFへの変換
Multi Page TIFFからPDFへの変換
アラートダイアログでPDFを表示
アラートダイアログ+WkWebViewでPDFを表示
アラートダイアログ+PDFViewでPDFを表示
5章 PDF処理 上級編
PDFのパスワード、暗号化設定
PDFのアクセス権とパスワード
パスワード設定をチェック
設定されているパーミッションを取得
PDFにパスワードを設定(1/2)
PDFにパスワードを設定(2/2)
PDFのパスワードを解除
PDFの空白ページ検出
PDFから空白ページを削除(1/3)
PDFから空白ページを削除(2/3)
PDFから空白ページを削除(3/3)
PDFフォーム入力
PDFフォームにテキスト入力して別名保存(1/2)
PDFフォームにテキスト入力して別名保存(2/2)
PDFフォームにチェックボックス入力(1/2)
PDFフォームにチェックボックス入力(2/2)
Quartzフィルタ
Quartzフィルタとは?
QuartzFilter出力例一覧
Quartzフィルタの一覧を出力
白黒のQuartzフィルタをかけて出力
ブルートーンのQuartzフィルタをかけて出力
PDFX-3のQuartzフィルタをかけて出力
グレートーンのQuartzフィルタをかけて出力
明度低下のQuartzフィルタをかけて出力
明度上昇のQuartzフィルタをかけて出力
ファイルサイズ縮小のQuartzフィルタをかけて出力
セピアトーンのQuartzフィルタをかけて出力
PDFアノテーションを取得、追加、書き換え、削除
アノテーションについて
アノテーションを取得
サークル・アノテーションを追加
スクウェア・アノテーションを追加
ライン・アノテーションを追加
テキスト・アノテーションを追加
URLリンク・アノテーションを追加(1/2)
URLリンク・アノテーションを追加(2/2)
指定語群にハイライトを追加(1/2)
指定語群にハイライトを追加(2/2)
指定語群にアンダーラインを追加(1/2)
指定語群にアンダーラインを追加(2/2)
指定語群に打ち消し線を追加(1/2)
指定語群に打ち消し線を追加(2/2)
PDFからすべてのアノテーションを削除
PDFのリンク抽出、リンク置換
PDFのリンク先は?
リンクの各種情報を取得(1/2)
リンクの各種情報を取得(2/2)
リンク・アノテーションからURLを抽出
URLリンク・アノテーションを追加(1/2)
URLリンク・アノテーションを追加(2/2)
書類内リンク・アノテーションを追加(1/2)
書類内リンク・アノテーションを追加(2/2)
リンクアノテーションのURLを置換
PDFのOCR処理(OCRテキスト埋め込み)
PDFのOCR処理(1/3)
PDFのOCR処理(2/3)
PDFのOCR処理(3/3)
PDFのしおり(TOC)を取得、追加、削除
TOCの題名と階層が悩ましい(1/2)
TOCの題名と階層が悩ましい(2/2)
指定PDFのTOCを取得してレコード化(1/2)
指定PDFのTOCを取得してレコード化(2/2)
ノンブルだけのフラットなTOCを付加(1/2)
ノンブルだけのフラットなTOCを付加(2/2)
Recordデータから階層TOCを付加(1/3)
Recordデータから階層TOCを付加(2/3)
Recordデータから階層TOCを付加(3/3)
Numbersの表データから階層TOCを付加(1/4)
Numbersの表データから階層TOCを付加(2/4)
Numbersの表データから階層TOCを付加(3/4)
Numbersの表データから階層TOCを付加(4/4)
指定PDFのTOCを削除
添付サンプルScript紹介
サンプルScript集
資料編
目下、「Cocoa Scripting Course #6 PDFKit」の仕上げ作業中ですが、懸案事項がありました。
PDFに対してコメント、注釈的なもの(PDFAnnotation)をつけられるようになっていますが、各アプリケーションで管理方法が微妙に異なっており、互換性がありません。
Adobe Acrobatが付けたアノテーション → Skimで表示できない、プレビュー.appで表示できる
Skimが付けたアノテーション → Adobe Acrobat、プレビュー.appで表示できない
プレビュー.appが付けたアノテーション → Adobe Acrobat、Skimで表示できる
▲Skim上でPDFに対して各種アノテーションを付加した状態
▲Skim上でPDFに各種アノテーションを付加したものをPreview.appで表示。何も表示されない
▲Skim上でPDFに各種アノテーションを付加したものをAdobe Acrobatで表示。こちらも何も表示されない
SkimとAcrobatのアノテーションの互換性がナニでアレでありますが、一応プレビュー.appとSkimがあればなんとかなる感じです。
さて、Skimでアノテーションを付けるとSkim同士でしかアノテーションを表示できない状態になってしまい、かつ、AppleScriptからPDFKitを操作しても読めないので困っていました(SkimのGUIアプリケーション経由で取得できないこともなさそう)。
Skimのアノテーションを読むために、Skim本体とは別にSkimNotes.frameworkとSkimNotesBase.frameworkが提供されています。Skim.appとは別にインストールする必要があります。インストール先は~/Library/Frameworksです。
コード署名されていないので、Script Debugger上でこれらのFrameworkの機能を呼び出そうとすると、署名されていない旨の警告が表示されますが、システム設定.app>セキュリティとプライバシーで認証できます。これら2つのFramrworkの両方とも認証しておく必要があります。
テストデータとして、Skim.app上でノートアノテーションをPDFに追記して保存。これに対して、AppleScriptからアクセスできるか試してみました。
結論からいえば、AppleScriptからアクセスできるわけですが、互換性を持たせるような何かがあってもいいような気がするところです。Adobe Acrobatでアノテーションを記載したPDFを、Skimで見られるように修正するとか。Skimで記入したアノテーションを他のアプリケーションでも見られるように変換するとか。
AppleScript名:Skim Notesのじっけん.scptd |
— – Created by: Takaaki Naganoya – Created on: 2023/08/17 — – Copyright © 2023 Piyomaru Software, All Rights Reserved — use AppleScript version "2.8" use framework "Foundation" use framework "PDFKit" use framework "SkimNotes" use framework "SkimNotesBase" use scripting additions set aPOSIX to POSIX path of (choose file of type {"com.adobe.pdf"}) set aURL to (current application’s |NSURL|’s fileURLWithPath:aPOSIX) set aPDFdoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL readSkimNotes:true set pCount to aPDFdoc’s pageCount() repeat with ii from 0 to (pCount – 1) log ii –Page Count set firstPage to (aPDFdoc’s pageAtIndex:ii) –> (PDFPage) PDFPage, label 1 set anoList to (firstPage’s annotations()) as list if anoList is not equal to {} then repeat with i in anoList set j to contents of i log j (*(SKNPDFAnnotationNote) Type: ’/Text’, Bounds: (320, 737) [16, 16]\n*) set aType to (j’s type()) as string log aType (*Note*) if aType = "Note" then set cVal to j’s |contents|() as string log cVal (*Skim Noteのタイトルだよ Skim Noteの本文だよ\n\n*) set cVal to j’s iconType() log cVal (*0*) set dVal to j’s isSkimNote() as boolean log dVal (*true*) if dVal = true then set eVal to j’s |string|() log eVal (*(NSString) "Skim Noteのタイトルだよ"*) set fVal to j’s SkimNoteProperties() (*(NSDictionary) {bounds:"{{308, 732}, {16, 16}}", color:(NSColorSpaceColor) sRGB IEC61966-2.1 colorspace 1 1 0.5 1, userName:"Takaaki Naganoya2", modificationDate:(NSDate) "2023-08-17 14:38:39 +0000", image:<NSImage 0x6000067d32a0 Size={1920, 1200} RepProvider=<NSImageArrayRepProvider: 0x6000015034a0, reps:(\n "NSBitmapImageRep 0x600003eb85b0 Size={1920, 1200} ColorSpace=(not yet loaded) BPS=8 BPP=(not yet loaded) Pixels=1920×1200 Alpha=YES Planar=NO Format=(not yet loaded) CurrentBacking=nil (faulting) CGImageSource=0x600001089360"\n)>>, contents:"Skim Noteのタイトルだよ", text:(NSConcreteAttributedString) Skim Note{\n NSFont = ""Helvetica 12.00 pt. P [] (0x11f597610) fobj=0x139642430, spc=3.33"";\n NSParagraphStyle = "Alignment 0, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ’(null)’";\n}の本文だよ\n\n{\n NSFont = ""HiraginoSans-W3 12.00 pt. P [] (0x11f597610) fobj=0x11f5277a0, spc=4.00"";\n NSParagraphStyle = "Alignment 0, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ’(null)’";\n}, type:"Note", pageIndex:0, iconType:0}*) set anImage to fVal’s image log anImage (*<NSImage 0x6000067cf700 Size={1920, 1200} RepProvider=<NSImageArrayRepProvider: 0x6000015182c0, reps:(\n "NSBitmapImageRep 0x600003effb10 Size={1920, 1200} ColorSpace=(not yet loaded) BPS=8 BPP=(not yet loaded) Pixels=1920×1200 Alpha=YES Planar=NO Format=(not yet loaded) CurrentBacking=nil (faulting) CGImageSource=0x60000105fa20"\n)>>*) set colRes to fVal’s |color| log colRes (*(NSColorSpaceColor) sRGB IEC61966-2.1 colorspace 1 1 0.5 1*) set tRes to fVal’s |text| log tRes (*(NSConcreteAttributedString) Skim Note{\n NSFont = ""Helvetica 12.00 pt. P [] (0x1296dd6e0) fobj=0x139642430, spc=3.33"";\n NSParagraphStyle = "Alignment 0, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ’(null)’";\n}の本文だよ\n\n{\n NSFont = ""HiraginoSans-W3 12.00 pt. P [] (0x1296dd6e0) fobj=0x11f5277a0, spc=4.00"";\n NSParagraphStyle = "Alignment 0, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ’(null)’";\n}*) set sBounds to fVal’s |bounds| log sBounds (*(NSString) "{{308, 732}, {16, 16}}"*) set uName to fVal’s userName log uName (*(NSString) "Takaaki Naganoya2"*) set modRes to fVal’s modificationDate log modRes (*(NSDate) "2023-08-17 14:38:39 +0000"*) end if end if end repeat end if end repeat |
Excelのワークシート上にドラッグ&ドロップして配置された画像のセルのアドレスを推定するAppleScriptの強化版です。
このぐらい作り込んでおけば実用性のある部品になることでしょう。Microsoft Excel 16.76を対象に検証を行なっています。テストは1つのExcel書類上に1つだけ画像を配置(Finderからドラッグ&ドロップで配置)した状態、1つのセルの上に乗っているように見える状態に大きさを調整して実行しました。
まず、大前提でpictureのID(1から始まる通し番号)、pictureの始点座標が存在するExcel上のセルのrow(行)とcolumn(列)。これはあらかじめ各Pictureの始点座標をもとにあらかじめ計算しておいたものを使います。
本Scriptは、この「始点座標から存在するセルを計算」する処理の後処理として、実際に画像が重なっているセルを重なっている部分の面積をもとに最大の面積のものを「配置されているセル」として計算で推定します。
この処理には前提条件があって、複数のセルにまたがりすぎているような(数十のセルにまたがってpictureが配置されている)場合には計算そのものが無意味です。「1セルに1画像ぐらい」の調子で配置されていないと、こうした処理を行なっても無駄になってしまいます。
本Scriptでは、Excelワークシートの対象のpictureのID、pictureの始点座標から求めたセルのrow(行)とcolumn(列)を与えて呼び出します。すると、始点セルを基準に3×3のセルの矩形エリア(NSRect)を求め、pictureの矩形(NSRect)との重なっている面積を計算。もっとも面積が大きいものを対象セルとしてrow, columnを返します。
こうしたScriptを作ってほうがよいと考えた理由は、このExcelシート上に貼り付けた画像に対してさまざまな文字データが隣のセルに入力され、セル上のデータと画像を関連づけてデータ取得するような処理を考えたときに必要と考えたためです。
AppleScript名:pictureが置かれているセルのアドレスを推定する v2.scpt |
— – Created by: Takaaki Naganoya – Created on: 2023/08/07 — – Copyright © 2023 Piyomaru Software, All Rights Reserved — use AppleScript use scripting additions use framework "Foundation" set targRow to 22 set targCol to 2 set targPictureID to 1 set tCell to getPictureLocatedCellRowColByItsPosition(targRow, targCol, targPictureID) of me –> {rowNum:23, columnNum:2} –ループで指定セルを左上とした3×3の範囲のセルから、指定のPictureと重なっている面積が最も大きいものを返す on getPictureLocatedCellRowColByItsPosition(targRow as integer, targCol as integer, targPictureID as integer) –Picture 1のNSRectを求める set pRect to getAPictureRectByID(targPictureID) of me set tmpList to {} –始点座標から3×3の範囲のセルとPictureの重なる面積を計算 repeat with x from 0 to 2 repeat with y from 0 to 2 set tmpRow to (targRow + y) set tmpCol to (targCol + x) set tmpRect to retExcelCellRect(tmpRow, tmpCol) of me –指定Pictureと指定Cellの共通部分の矩形座標を計算 set a1Res to (current application’s NSIntersectionRect(tmpRect, pRect)) as {record, list} –指定Pictureと指定Cellの共通部分の面積を計算 set a1Area to calcAnArea(a1Res) of me if a1Area > 0 then set the end of tmpList to {columnNum:tmpCol, rowNum:tmpRow, interAreaWithPict:a1Area} end if end repeat end repeat –面積で降順ソート set zList to (sortRecListByLabel(tmpList, {"interAreaWithPict"}, {false}) of me) as list –> {{columnNum:2, rowNum:23, interAreaWithPict:4973.377807617188}, {columnNum:2, rowNum:24, interAreaWithPict:747.665495456778}, {columnNum:2, rowNum:22, interAreaWithPict:333.296168677043}} set resCell to first item of zList set resCol to columnNum of resCell set resRow to rowNum of resCell return {rowNum:resRow, columnNum:resCol} end getPictureLocatedCellRowColByItsPosition –IDで指定したPictureのNSRectを返す on getAPictureRectByID(anID as integer) tell application "Microsoft Excel" tell active workbook tell active sheet set anImage to picture anID set tmpX to left position of anImage set tmpY to top of anImage set tmpW to width of anImage set tmpH to height of anImage end tell end tell end tell set aZRect to current application’s NSMakeRect(tmpX, tmpY, tmpW, tmpH) return aZRect end getAPictureRectByID –NSRectの面積を計算する on calcAnArea(aRect) if class of aRect = list then set xWidth to (item 1 of item 2 of aRect) set yHeight to (item 2 of item 2 of aRect) else set xWidth to (aRect’s |size|’s width) set yHeight to (aRect’s |size|’s height) end if set anArea to xWidth * yHeight return anArea end calcAnArea –指定Row, ColumnのCellのNSRectを返す on retExcelCellRect(y as integer, x as integer) tell application "Microsoft Excel" tell active workbook tell active sheet tell row y tell cell x set xMin0 to left position set yMin0 to top set xWidth0 to width set yHeight0 to height end tell end tell end tell end tell end tell set a1Rect to current application’s NSMakeRect(xMin0, yMin0, xWidth0, yHeight0) return a1Rect end retExcelCellRect –リストに入れたレコードを、指定の属性ラベルの値でソート on sortRecListByLabel(aRecList as list, aLabelStr as list, ascendF as list) set aArray to current application’s NSArray’s arrayWithArray:aRecList set aCount to length of aLabelStr set sortDescArray to current application’s NSMutableArray’s new() repeat with i from 1 to aCount set aLabel to (item i of aLabelStr) set aKey to (item i of ascendF) set sortDesc to (current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabel ascending:aKey) (sortDescArray’s addObject:sortDesc) end repeat return (aArray’s sortedArrayUsingDescriptors:sortDescArray) end sortRecListByLabel |
Excelのワークシート上にドラッグ&ドロップして配置された画像のセルのアドレスを推定するAppleScriptの試作品です。
ただし、pictureオブジェクトは単にExcel書類上に重ね合わせて配置されている「異物」であり、どこかのセルに置かれているわけではありません。オブジェクトからpropertiesを取得しても、セルのアドレスを取得できたりはしません。ワークシート左上を原点とする座標値が取得できるだけです。
ただ、pictureの配置座標を取得して、その座標からセルを求める処理を行ったとしても(やりました)、配置座標と実際に置かれているセルは異なります。
実際に、Excelワークシート上に配置されている画像を個別に書き出す処理(リサイズして高解像度化する処理つき)を書いて動かしてみましたが、書き出したファイル名にセルのアドレスを反映させたら使いやすいのではないかと考え、解決策を試してみました。
上の画面キャプチャでいえば、R22C2のセルにpicture 1が存在している(始点座標が存在している)わけなのですが、実際に目で見た感じではR23C2に存在している(ユーザーがそのように作業を行った)ものと推定されます。この画像をファイル書き出しする際に、R22C2というファイル名をつけてしまっては、後で整理する際に「ナニコレ?」という話になってしまいます。
そこで、複数のセルにまたがってpictureオブジェクトの矩形領域と、それぞれ重なっているセルの矩形領域の共通部分を求め、面積を計算。共通面積が最も大きいものを採用するための試作品を作ってみた次第です。
AppleScript名:pictureが置かれているセルのアドレスを推定する.scpt |
— – Created by: Takaaki Naganoya – Created on: 2023/08/07 — – Copyright © 2023 Piyomaru Software, All Rights Reserved — use AppleScript use scripting additions use framework "Foundation" set aRect to retExcelCellRect(22, 2) of me set bRect to retExcelCellRect(23, 2) of me tell application "Microsoft Excel" tell active workbook tell active sheet set anImage to picture 1 set tmpX to left position of anImage set tmpY to top of anImage set tmpW to width of anImage set tmpH to height of anImage end tell end tell end tell set aZRect to current application’s NSMakeRect(tmpX, tmpY, tmpW, tmpH) –共通部分の矩形座標を計算 set a1Res to (current application’s NSIntersectionRect(aRect, aZRect)) as {record, list} set b1Res to (current application’s NSIntersectionRect(bRect, aZRect)) as {record, list} –共通部分の面積を計算 set a1Area to calcAnArea(a1Res) of me set b1Area to calcAnArea(b1Res) of me –大小判定 if a1Area > b1Area then return "a is larger" else return "b is larger" end if –NSRectの面積を計算する on calcAnArea(aRect) if class of aRect = list then set xWidth to (item 1 of item 2 of aRect) set yHeight to (item 2 of item 2 of aRect) else set xWidth to (aRect’s |size|’s width) set yHeight to (aRect’s |size|’s height) end if set anArea to xWidth * yHeight return anArea end calcAnArea on retExcelCellRect(y, x) tell application "Microsoft Excel" tell active workbook tell active sheet tell row y tell cell x set xMin0 to left position set yMin0 to top set xWidth0 to width set yHeight0 to height end tell end tell end tell end tell end tell set a1Rect to current application’s NSMakeRect(xMin0, yMin0, xWidth0, yHeight0) return a1Rect end retExcelCellRect |
マンデルブロ集合を文字で組み立てて、デスクトップフォルダにRTF形式で保存して、テキストエディットでオープンして表示するAppleScriptです。
Courier Newはどの環境にも入っているフォントだと思っていますが、ない場合には別の等幅フォントのPostScript名に変更してください。また、フォントサイズや描画色を変更してみるといいかもしれません。
ちなみに、実用性はまっっっっったくありません。昔は計算に数分かかったのに、いまだとインタプリタ型の言語で動かしても1秒以下なんだ、へーという納得ができる程度です。
AppleScript名:マンデルブロ集合を描画してRTFとして組み立ててテキストエディットでオープン.scpt |
— – Created by: Takaaki Naganoya – Created on: 2023/08/06 — – Copyright © 2023 Piyomaru Software, All Rights Reserved — use AppleScript use framework "Foundation" use framework "AppKit" use scripting additions property NSFont : a reference to current application’s NSFont property NSUUID : a reference to current application’s NSUUID property NSColor : a reference to current application’s NSColor property NSString : a reference to current application’s NSString property NSDictionary : a reference to current application’s NSDictionary property NSLiteralSearch : a reference to current application’s NSLiteralSearch property NSMutableArray : a reference to current application’s NSMutableArray property NSMutableDictionary : a reference to current application’s NSMutableDictionary property NSFontAttributeName : a reference to current application’s NSFontAttributeName property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName property NSDocumentTypeDocumentAttribute : a reference to current application’s NSDocumentTypeDocumentAttribute set outStr to generateMandel() of me set anAssrStr to makeRTFfromParameters(outStr, "CourierNewPSMT", 12) of me –結果のRTFをデスクトップ上に書き出す。ファイル名はUUID.rtf set thePath to (POSIX path of (path to desktop)) & (do shell script "uuidgen") & ".rtf" set aRes to my saveStyledTextAsRTF(thePath, anAssrStr) set targAlias to (POSIX file (thePath as string)) as alias tell application "TextEdit" activate open targAlias end tell –スタイル付きテキストを指定パス(POSIX path)にRTFで書き出し on saveStyledTextAsRTF(targPath, aStyledString) set targPathNSString to NSString’s stringWithString:targPath set bstyledLength to aStyledString’s |string|()’s |length|() set bDict to NSDictionary’s dictionaryWithObject:"NSRTFTextDocumentType" forKey:(NSDocumentTypeDocumentAttribute) set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict return (bRTF’s writeToFile:targPath atomically:true) as boolean end saveStyledTextAsRTF –書式つきテキストを組み立てる on makeRTFfromParameters(aStr as string, aFontName as string, aFontSize as real) set aVal1 to NSFont’s fontWithName:aFontName |size|:aFontSize set aKey1 to (current application’s NSFontAttributeName) set aVal2 to NSColor’s cyanColor() set aKey2 to (current application’s NSForegroundColorAttributeName) set aVal3 to 0.0 set akey3 to (current application’s NSKernAttributeName) set keyList to {aKey1, aKey2, akey3} set valList to {aVal1, aVal2, aVal3} set attrsDictionary to NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList set attrStr to NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary return attrStr end makeRTFfromParameters –マンデルブロ集合を文字で描画して返す on generateMandel() set outStr to "" repeat with y from -12 to 12 by 1 repeat with x from -39 to 39 by 1 set ca to x * 0.0458 set cb to y * 0.08333 set a to ca set b to cb repeat with i from 0 to 15 by 1 set t to (a * a) – (b * b) + ca set b to (2 * a * b) + cb set a to t if ((a * a) + (b * b)) > 4 then exit repeat end repeat if ((a * a) + (b * b)) ≤ 4 then set outStr to outStr & " " else if i > 9 then set i to i + 7 set outStr to outStr & string id (48 + i) end if end repeat set outStr to outStr & return end repeat return outStr end generateMandel |
日常的に利用するAppleScriptは、macOS標準搭載のScript Menuに入れて使用しています。エンドユーザーに「Script Editor上で実行しろ」とかいうのは無茶ですし、「全部Appletに書き出してアプリケーションとして実行」とかいうのも、セキュリティ的な縛りが増えた環境においては、無茶な話です。だいたい、日常的に利用している数百本のAppleScriptを全部Appletに書き出すというのも(自動処理でできるけど)無茶な話です。
そのため、日常的に利用するAppleScriptは、個人的にScript Menuから実行していますし、Script Menuを「最終防衛線」として定義し、AppleがOSアップデートのたびに作成するバグに対して文句を言っています。Script Menu上で動かなかったら問題視しています。
さて、そこに新たな頭痛の種が舞い込んできました。ここ数日いじくりまわしているPowerPointです。
いつものように、Script Menuに操作系のScriptを入れて実行すれば、それでおしまい! というわけには行きませんでした。PowerPointの書類からTOCつきのPDFを書き出すAppleScriptを書いて、Script Menuに入れたら実行できなくなりました(途中までは実行できるのに……)。Script Editor/Script Debugger上では問題なく実行できるのに、です。
この場合、Script Menuがランタイム環境(実行環境)になるわけで、各種セキュリティ設定もScript Menuに対して行っています。AppleScript Appletを数百個自動生成できたとしても、数百個のAppletに対してすべてセキュリティ設定を行うのは現実的な話ではありません。Script Menuに対して権限設定をまとめておけたほうが便利です。
そのため、Script Menuには「オートメーション」「フルディスクアクセス」「アクセシビリティ」など考えられる一通りのセキュリティ設定が行われているわけですが、先のPowerPointを操作するAppleScriptが、Script Menu上からは実行が完了しませんでした。
どうもファイルアクセス権限に関する問題(Script Editor上から実行しても、初回はダイアログが出る)のようなのですが、この権限がScript Menuから実行すると取得できないようで…
この問題をAppleに報告すべきなのか、Microsoftに報告すべきなのか、現状だと判断がつかないところです(たらいまわしにされる予感)。
PowerPointなんか使う方が悪い、という話までありそうな、、、、
PowerPoint書類(presentation)の各スライドのタイトルを取得するAppleScriptです。
正確にいえば、タイトルを取得するかもしれないAppleScriptです。本Scriptの実行時にはPowerPointで何らかのPPTX書類をオープンしていることを期待しています。
PowerPointをこづき回してみると、各スライドのタイトルを保持しているプロパティとかいったものが「ない」ことに気づきます。
ではどうやって取り出すかといえば、
(1)slideのplace holderを取得する
(2)place holder内にtext frameが存在しているかを確認
(3)text frameが存在している場合には、内部にアクセスして文字を取り出す
という手順になるようです。
ただし、place holderにアクセスする都合上、
スライドのレイアウトの種類によってはplace holderが存在していないものもあるため、place holderの存在確認から行うべきかもしれません。
また、slide内に複数のplace holderが存在する場合に、どれがtitleに該当するのかを調べる必要があるとか(座標とか、文字サイズとかを頼りに推測)、いろいろと処理が破綻しそうな「例外条件」が多数存在していそうです。
AppleScript名:各slideのタイトル文字列を取得 v2.scpt |
— – Created by: Takaaki Naganoya – Created on: 2023/08/03 — – Copyright © 2023 Piyomaru Software, All Rights Reserved — set tList to getEveryPPTSlideTItles() of me –> {"Title", "1章", "Slide1", "Slide2", "2章", "Slide3", "Slide4"} on getEveryPPTSlideTItles() set tList to {} tell application "Microsoft PowerPoint" tell active presentation set sList to every slide repeat with i in sList set j to contents of i tell j set plaList to every place holder set aPla to contents of first item of plaList set hText to (has text of text frame of aPla) as boolean if hText = true then set hTextR to (content of text range of text frame of aPla) as string else set hTextR to "" end if end tell set the end of tList to hTextR end repeat return tList end tell end tell end getEveryPPTSlideTItles |
いろいろ調べてみたら、PowerPointが扱うファイルパス形式に整合性がまったくないことがわかりました。なんなんでしょう、これは。
これは、とくに問題はありません。最低限のラインはクリアしているといってよいでしょう。
fileオブジェクトではなくHFS pathの文字列です。ここでかなり「おかしなプログラムだな」という感想を抱きます。こんなおかしなデータを要求するのはPowerPointぐらいだと思いますよ?
PDF書き出しもPPTX書類の保存も、同じく「save」コマンドで行うので、仕様が同じなのも納得ですが、通常書類のsaveにこれでは相当変わった仕様にしか見えません。
これは、致命的におかしな挙動であり、呆れるほどおかしな仕様です。担当者が正気なのか疑わしいレベルです。Office 2011のPowerPointでpresentation(書類)のfull name(フルパス情報)を取得してみたところ、HFS path文字列が返ってきたという調査結果が残っていました。
いま、バージョン16.75のPowerPointのpresentation(書類)のfull name(フルパス情報)を取得すると、POSIX pathが返ってきます。
▲Office 2011のPowerPointのパス情報の記述
▲バージョン16.75のPowerPointのパス情報の記述
AppleScript用語辞書上の記載内容にはたいして変化はないのですが、こんな頭のおかしな状態になっているとは思いませんでした。正直、PowerPointで何かまとまった処理を行おうとは思ったことが(それほど)なかったのですが、Keynoteで山のようにいろいろ強烈なScriptを書いているので、PowerPointでもいろいろできるのでは? と、冗談半分で試してこの通りです。
まさか、ExcelとWordもこの調子なのでは?(^ー^;
AppleScript名:オープン中の最前面のPowerPoint書類のフルパスの文字列を取得.scpt |
set a to getPPTpath() of me –> "Cherry:Users:me:Documents:2013-MacUDingCFUD.ppt"–Office 2011 –> "/Users/me/Documents/AppleScript 13/PowerPoint/TESTプレゼンテーション1 .pptx"–Office 2019 –オープン中の最前面のPowerPoint書類のフルパスの文字列を取得 on getPPTpath() tell application "Microsoft PowerPoint" set pCount to count every presentation if pCount = 0 then return false tell active presentation –Documentのフルパスを取得する set aPath to full name return aPath end tell end tell end getPPTpath |
PowerPointの書類からTOCつきのPDFを書き出すAppleScriptを書いた際には、頭のおかしなPowerPoint 16.75が返してくるパス形式をサブルーチン側で吸収して処理するようにしました。ただ、将来的にこの頭のおかしな形式からまともな形式に戻してきたときに問題が発生するので、再変更に備えてもう少し準備しておいたほうがよいのかもしれません。
このPowerPointの担当者は、頭がおかしいです。
AppleScript名:オープン中のPowerPoint書類のパスをalias形式で取得.scpt |
set pptPath to getPPTpath() of me –> alias "Macintosh HD:Users:me:Documents:AppleScript 13:PowerPoint:TESTプレゼンテーション1 (Sectionなし).pptx" –オープン中の最前面のPowerPoint書類のフルパスの文字列を取得 on getPPTpath() tell application "Microsoft PowerPoint" set pCount to count every presentation if pCount = 0 then return false tell active presentation –Documentのフルパスを取得する set aPath to full name end tell end tell set aFile to POSIX file aPath set anAlias to aFile as alias return anAlias end getPPTpath |
KeynoteからPDF書き出しを行う際に、デフォルトの機能ではTOCも何もついていないのですが、AppleScriptからあらゆる手段を講じてTOCつきで書き出せるようにしています(新刊「Keynote Scripting Book with AppleScript」に掲載)。
一方、PowerPointではどうかといえば、sectionを作成し章構成を分けて、スライドを章ごとに折りたためるようになっています。PDF書き出し時にこのsectionが反映されるということはまったくなく、sectionを追加しようが書き出されたPDFはそのままです。
このsection内のインデント情報が取得できれば、それを元にTOCを作ってもよいのですが、残念ながらインデント情報は取り出せないようです。
ただ、処理に必要な最低限の情報が取れるので、Keynoteと同レベルのTOCつきPDFをAppleScriptで合成することは可能と思われます。
AppleScript名:各スライドから情報を取得.scpt |
tell application "Microsoft PowerPoint" tell active presentation set sList to every slide repeat with i in sList set j to contents of i tell j set sInd to section index set sNum to section number set myLayout to layout as string log {sInd, sNum, myLayout} end tell end repeat end tell end tell |
各スライドのタイトルを取得しようとしたら、素直に取得できず……かといって取れなさそうでもないので、いろいろ調べてみたら、どうやら取得できたようです。
AppleScript名:各slideのタイトル文字列を取得.scpt |
tell application "Microsoft PowerPoint" tell active presentation set sList to every slide repeat with i in sList set j to contents of i tell j set sInd to section index set sNum to section number set myLayout to layout as string log {sInd, sNum, myLayout} set plaList to every place holder set aPla to contents of first item of plaList set hText to (has text of text frame of aPla) as boolean log hText if hText = true then set hTextR to (content of text range of text frame of aPla) as string log hTextR end if end tell end repeat end tell end tell |
Piymaru Softwareによる電子書籍の82冊目、「Keynote Scripting Book with AppleScript」を発売しました。PDF 434ページ+サンプルScript Zipアーカイブで構成されています。macOS標準装備のScript Menuに入れてKeynoteをコントロールするメニューScript集も添付しています。
→ 販売ページ
初級、中級、上級のScripting解説内容に加え、大量かつ強力なAppleScriptサンプルを掲載しています。これは、実際に筆者が自分のマシンにインストールして使っているものと同じものです。
実用編
付録Script Menuスクリプトのインストール
Script Menuスクリプトのインストール①
Script Menuスクリプトのインストール②
TOCつきでPDF書き出し
最前面の書類をTOCつきPDF出力①
最前面の書類をTOCつきPDF出力②
最前面の書類をTOCつきPDF出力③
すべてデスクトップ上にPDF出力
クリップボードに入れたものを処理
指定色でモノクロ化してクリップボードへ
赤でモノクロ化してクリップボードへ
水色でモノクロ化してクリップボードへ
グリーンでモノクロ化してクリップボードへ
マゼンタでモノクロ化してクリップボードへ
自動スライドめくり
冒頭から末尾まで自動スライドめくり
表を処理
表オブジェクトの重なり合い(2つ以上対応)を検出
AppleScript Recordから表を作成
選択中のセルの文字列長さを一覧表で表示
全スライドの表で行番号入り表の連番修正
現在のスライド上にある選択中の表のカラム幅を自動調整
現在のスライド上にあるすべての表のカラム幅を自動調整
選択中のページのオリジナル表のセル幅に以降の表のセル幅を統一
選択中のページの表1のセル幅に以降の表のセル幅を統一
現在のスライド上にあるすべての表のカラム幅を自動調整
マル付き数字のインクリメント(+1)
マル付き数字のデクリメント(ー1)
マル付き数字のリナンバー
現在のスライドからテキストアイテムを色で選択
x色系のみ選択
選択中の最前面の書類に対する処理
連続する同一タイトルに子番号を振る
スライド末尾にQRコードを追加
選択中の画像へのぼかしフィルタ処理
選択中の画像にぼかし処理
選択中の各種オブジェクトへの処理
オブジェクトの重なり合い(2つペア)を検出してグループ化
テキストを座標値でソートしてまとめてクリップボードへ
横幅を均等に変更
数える
選択中のオブジェクトのうちテキストだけ残す
現在表示中のスライドに対する処理
テキストを上から順に連結してクリップボードに転送
指定した文字で囲まれたキーワードの色を置換する
PDFに書き出してオープン
選択中のテキストアイテムの処理
AppleScriptとみなして、構文確認してスタイル付きテキストとして書き戻す
AppleScriptとみなして、構文確認してスタイル付きテキストとして書き戻す(結果つき)
それぞれ個別のAS書類として保存する
まとめて1つのAS書類として保存する
テキストアイテム中のURLにリンクを張って
HTMLタグを外す
文字の実際の幅でリサイズ
文字の実際の幅と高さでリサイズ
選択中のスライドの処理
すべての表の文字色を黒にする
タイトルをまとめてクリップボードに入れる
現在のスライド以降の同じスライドレイアウト(同階層の)スライドのタイトルを取得してクリップボードへ
現在のスライド以降の同じスライドレイアウト(同階層)のスライドのタイトルを取得して改行でまとめてクリップボードへ(末端レベルではないものが対象)
選択中のスライドのグループアイテムをグループ解除
選択中のスライド中の各テキストアイテムのうち指定フォントサイズのものの色を変更
不透明度が100でないオブジェクトの透明度を解除
選択スライド枚数を数える
選択中のスライドのshapeのうち最も右上にあるものの座標を右上角に変更
目次作成
クリップボードへ転送
Numbersの表を作成
カレンダー作成
指定月の日曜日はじまりカレンダーを表で作成
オブジェクトの詰め込み
指定範囲にスライド上のオブジェクトを詰め込み①
指定範囲にスライド上のオブジェクトを詰め込み②
2ページ間(現在、次)のオブジェクト整頓
オブジェクトの位置をそろえる
没スライドに対する処理
現在の書類と同じ階層に没スライド入れを作成
指定画像をbase64でエンコードしたのちに、デコードして画像に戻すAppleScriptです。
base64文字列からのデコードだけをテストしたかったのですが、テストのために本来は不要な画像のエンコード部分を付け足しています。
macOS 13.5上で動作確認していますが、OSのバージョンやAppleScriptのバージョンに依存はしないことでしょう。
AppleScript名:指定画像をbase64エンコード文字列に変換→デコード.scptd |
— – Created by: Takaaki Naganoya – Created on: 2023/07/29 — – Copyright © 2023 Piyomaru Software, All Rights Reserved — property NSData : a reference to current application’s NSData property NSString : a reference to current application’s NSString property NSImage : a reference to current application’s NSImage property NSPNGFileType : a reference to current application’s NSPNGFileType property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding property NSDataBase64EncodingEndLineWithLineFeed : a reference to current application’s NSDataBase64EncodingEndLineWithLineFeed use AppleScript version "2.8" — macOS 12 or later use framework "Foundation" use scripting additions set aFile to choose file of type {"public.image"} set aStr to base64StringFromImageFile(aFile) of me –> "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAKuWlDQ1BJQ0MgU……" set aImage to decodeImageFromBase64String(aStr) of me –> (NSImage) –Base64 Decode on decodeImageFromBase64String(aString) set restoreData to NSData’s alloc()’s initWithBase64EncodedString:aString options:0 set restoreImage to NSImage’s alloc()’s initWithData:restoreData return restoreImage end decodeImageFromBase64String –Base64 Encode on base64StringFromImageFile(aFile) set aPOSIX to POSIX path of aFile set anImage to NSImage’s alloc()’s initWithContentsOfFile:aPOSIX set imageRep to NSBitmapImageRep’s alloc()’s initWithData:(anImage’s TIFFRepresentation()) set aPNGdat to imageRep’s representationUsingType:(NSPNGFileType) |properties|:(missing value) set base64Str to aPNGdat’s base64EncodedDataWithOptions:(NSDataBase64EncodingEndLineWithLineFeed) set bStr to (NSString’s alloc()’s initWithData:base64Str encoding:(NSUTF8StringEncoding)) return bStr as string –or return NSString (delete as string) for speedy processing end base64StringFromImageFile |
よくある、Microsoft PowerPointで、オープン中の最前面の書類を指定のパスにPDFで書き出すAppleScriptです。M1 Mac mini+macOS 13.5上で動作するMicrosoft PowerPointバージョン16.75で確認しています。
スクリプトエディタ上で動かしてみましたが、デスクトップ上にファイルを書き込もうとしたときに、初回のみPowerPointがデスクトップフォルダにアクセスしてよいか、OSが確認ダイアログを表示してきました。こういう動作はScriptからのコントロール時には困ります。自動処理を行う前にクリアしておきたい(あらかじめダイアログ表示+認証を済ませておきたい)ところです。
Microsoft PowerPointのAppleScript用語辞書を確認してみたところ、「export」コマンドが存在しません。情報をいろいろ調べてみたところ「save as PDF」で指定パスにPDFを保存するようです。
さらに、こうした場合にsaveコマンドで指定するファイルパスは、fileオブジェクトで指し示しますが、PowerPointでは少々事情が異なるようでした。fileを指定すると、書き出されません。HFS形式のパスをテキストで指定する必要がありました。どうもこのあたり、ところどころバグめいた実装が散見されます。
Excelへの画像貼り込みScriptのように、POSIX pathでPDF書き出し先を指定してみましたが、こちらはPOSIX pathで指定しても書き出せませんでした。
AppleScript名:最前面の書類をPDF書き出し.scpt |
set theOutputPath to ((path to desktop folder) as string) & (do shell script "uuidgen") & ".pdf"
tell application "Microsoft PowerPoint" tell active presentation save in theOutputPath as save as PDF –保存先のファイルパスを文字列で指定する必要がある –fileオブジェクトで指定すると書き出されない(バグのような挙動) end tell end tell |
Piyomaru Softwareの電子書籍も、いまでは80冊ほど出していますが、最初のきっかけは「AppleScript最新リファレンス」という名前の薄い本(500ページぐらい)でした。
▲計画段階の「AppleScript最新リファレンス」。ここから、実際に書けない記事をふるい落とすという「妥協」が…….
その後、何回かアップデート計画が浮上しては、あまりの分量の多さの前に挫折。ゼロから書くよりも、アップデートする作業の方が実は大変です。
「AppleScript最新リファレンス」はいわば何でもありの「大辞典」に相当するものです。「これでは更新し切れないねー」とあきらめて、小さなテーマごとに最新情報を集めて各種電子書籍を書くという「小辞典」「中辞典」方式を展開。
項目数の少ない「小辞典」「中辞典」で情報のリストアップと更新を行い、その結果を「大辞典」に反映させるというやりかたです。
ここに来て、ようやく更新できるんじゃないか?(仮説)という雰囲気になってきたため、更新をすすめています。まえがきとあとがきを書き、その他のコンセプトに関わるページを書いてみて「いけそうかも」という手応えを得ています(挫折する可能性はあるかも)。
「AppleScript最新リファレンス」はMacDownとPages(扉ページや凝ったレイアウトが必要なページだけ)を用いて作成したのですが、記述環境としてのMacDownがあまりにも使えないので、MarkdownのページはPagesで作り直しています。
最新版のmacOSが動くマシンが手元にM1 Mac miniの1台しかないので、これにBeta版のmacOS 14などを入れてしまった日にはスクリーンショットが撮れません。正確に言えば、スクリーンショットを撮っても使えません(禁止事項)。
なので、外付けSSDへのベータ版のmacOSのインストールは必須なのですが、macOS 13の際に「リリース後には、外付けSSDからの複数バージョンのOSのインストールや起動を禁止する」というジャイアン風味たっぷりなAppleの対応。さらに、β版のmacOSよりもRelease版のmacOSの方がバグだらけで(OS X 10.13、10.15にひきつづき3回目)、macOS 14についてもβプログラムの存在意義について疑問視しています。
とにかく、macOS 14についてはβ版をガン無視して「AppleScript最新リファレンス」のアップデートを行うべき、という判断です。
Claris FileMaker Pro 2023と、Microsoft Officeの最新版については、AppleScript記述方法に関する記事は掲載したいところです(Developer Program入ってるし、Office 365の契約しているし)。Adobe Creative Cloudについては契約していないので、このあたりの話は書けません。
AppleScriptコマンドリファレンスについては、各種書籍用にアップデートしながらメンテナンスしているので、これは最新版を掲載できることでしょう。エラーコード表も然りです。
全体的に、Script Sampleを掲載しまくるような本ではなく、それぞれの項目について「最新環境ではどうなっているのか」という情報を提供する書籍になることでしょう。
そして、挫折しなければv2.8対応版を出せることでしょう。
→ AppleScript最新リファレンス v2.8対応(日本語版)
→ AppleScript The Latest Reference for v2.8 (1) (English Version)