Keynoteでページ数の多い書類、この場合は電子書籍「Cocoa Scripting Course」の9巻「File Processing」で章ごとの目次を作ろうとしたときに、各ページのタイトルを記事のレベルを反映しつつ文字列化するのが大変なので、AppleScriptで半自動化しました。
実際には、機械的に全スライドのタイトルを取得してテキスト化するAppleScriptを作って使っていたのですが、機械的にタイトルを取り出したあとで、レベルを反映させるといった「手作業」が発生していたので、半自動処理に変更したほうがまだ作業量が少なくなるという見立てになりました。
Keynote書類をオープンして、最終階層のスライドを畳んだ状態で処理範囲(同一レベルのスライド)を選択しておいた状態で本Scriptを実行します。もともと、章トビラに掲載する内容をテキストで取り出すためのものなので、1つの章を構成する中トビラのみを選択した状態にしてから実行する必要があります。
Keynoteでは、ウィンドウ左端にナビゲータを表示させ、各スライドのレベルを変更し、アウトラインプロセッサのように下位スライドの表示を隠したり、表示させたりできます。つまり、ページ数の多いスライドの整理を行いやすくなっています。
本来、このスライドのインデントレベルを数値で取得できる必要があります。それでも、KeynoteのAppleScript用語辞書にその機能は実装されていません。ただ、ないものにないと文句を言っているだけでは前に進めません。
Keynoteのウィンドウ上で下位スライドを非表示状態にして選択すると、selection中の第1階層のスライドのみ選択された状態になるため、スライド番号を取得すると、selection中の第2階層のスライド番号が「抜けた」状態になります。この違いによってスライドの階層を識別できます。
スライド番号を取得できたselection中の第1階層のスライドと、スライド番号を取得できなかったselection中の第2階層のスライドで別々に処理(インデント文字=tab)することで、文字列で階層の違いも表現できます。
出力例:
◽️再帰処理によるファイル検索 –第1階層
再帰によるファイルパスの取得 –第2階層
再帰によるファイルパスの取得+拡張子による抽出 –第2階層
ファイル名衝突回避つきリネーム –第2階層
処理結果はクリップボードに転送されます。つまり、他のテキストエディタに内容をそのままペーストできます。
スライドのインデントレベルを取得できないことへの対処は、スライドのbase layoutにインデント情報を書いておくとか、種別で判定するやりかたもあります。出現確率をそれぞれのbase layoutについて計算し、登場頻度の低いものをより上位の章トビラとして認識するといったAppleScriptもありますが、完全自動よりも決定前にユーザーに確認を取るぐらいの作りになっているほうが実用性が高い感じです。
AppleScript名:選択中のスライドから、2段階の階層のタイトルを取得してクリップボードへ.scptd |
— – Created by: Takaaki Naganoya – Created on: 2024/05/04 — – Copyright © 2024 Piyomaru Software, All Rights Reserved — use AppleScript use framework "Foundation" use scripting additions property headerChar : "◽️" –1階層目のタイトルの行頭に入れる property indentChar : tab –2階層目のタイトルの行頭に入れる tell application "Keynote" tell front document set aSel to selection set aFirst to class of first item of aSel if aFirst is not equal to slide then display dialog "スライド単位で選択されていません(スライド内のオブジェクトが選ばれている)" buttons {"OK"} default button 1 return end if –スライドの逆順選択が発生していた場合には、listを逆順に入れ替えて正順(開始ページ→終了ページ)に修正 set fItem to slide number of first item of aSel set lItem to slide number of last item of aSel if fItem > lItem then set aSel to reverse of aSel –スライド番号を選択部分から取り出す set pNumList to {} repeat with i in aSel set the end of pNumList to (slide number of i) end repeat –選択中の最終スライドの次のスライドは説明部分とみなし、説明部分が続く(同じベースレイアウト)ブロックを検出 set totalCount to count every slide set aMasterSlide to base layout of slide ((last item of pNumList) + 1) set hitF to false repeat with i from (last item of pNumList) + 2 to totalCount set tmpBase to base layout of slide i if aMasterSlide is not equal to tmpBase then set hitF to true exit repeat end if end repeat if hitF = false then error "Page selection Error" —末尾の情報を補う。そのため、本Scriptではスライド末尾までの選択が行えないが、実用上問題がないので無視している copy i to lastThreadShould copy pNumList to pNumList2 set the end of pNumList to i –メインループ set outList to {} set aCount to 1 –1階層目のタイトル収集ループ repeat with i in (pNumList2) tell slide i try set tmpTitle1 to object text of default title item on error set tmpTitle1 to "" –タイトルが存在しないスライドなどの場合 end try end tell –オブジェクト内の強制改行文字を削除 set tmpTitle13 to repChar(tmpTitle1, string id 8232, "") of me –出力用のリストに追加 set the end of outList to (return & headerChar & tmpTitle13) –2階層目のタイトル収集ループ repeat with ii from (i + 1) to ((item (aCount + 1) of pNumList) – 1) if ii ≥ (lastThreadShould) then exit repeat tell slide ii try set tmpTitle2 to object text of default title item on error set tmpTitle2 to "" –タイトルが存在しないスライドなどの場合 end try end tell –オブジェクト内の強制改行文字を削除 set tmpTitle22 to repChar(tmpTitle2, string id 8232, "") of me –出力用のリストに追加 set the end of outList to (indentChar & tmpTitle22) end repeat set aCount to aCount + 1 end repeat –クリップボードに結果文字列を設定 set outStr to retListedText(outList, return) of me set the clipboard to outStr end tell end tell –1D Listにデリミタ文字を入れつつテキスト化 on retListedText(aList, aSeparator) set aText to "" set curDelim to AppleScript’s text item delimiters set AppleScript’s text item delimiters to aSeparator set aText to aList as text set AppleScript’s text item delimiters to curDelim return aText end retListedText –文字置換ルーチン on repChar(origText as string, targStr as string, repStr as string) set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr} set temp to text items of origText set AppleScript’s text item delimiters to repStr set res to temp as text set AppleScript’s text item delimiters to txdl return res end repChar |