オープンソースのPDFビューワー「Skim」がv1.7.9にアップデートしました。AppleScript用語辞書に変更が加わっていますが、説明文が追加された程度であり機能面での変更は見られません。
タグ: Skim
Skim v1.7.6でopen locationコマンドを実装するも動作せず
オープンソースのPDFリーダー「Skim」のバージョン1.7.6において「open location」コマンドが実装されましたが、実際に試してみると動きません。確認は最新版の1.7.7で行いました。
これまでにも追加したコマンドを次のバージョンで廃止して、翌々バージョンで復活させたりと、いろいろその歴史をひもとくと「おっとっと」な動きが見られるので、そういうものなんでしょう。
本Blog上に存在するPDFのURLを指定。open locationでダウンロード+表示を試みるものの、表示されずにエラーになる。
→ Skimが「http://」をサポートしていないとのこと。https://からのダウンロードおよび表示は確認しました。
AppleScript名:Skim v17.6で追加されたopen locationのテスト.scpt |
set aURL to "http://piyocast.com/as/wp-content/uploads/2018/09/GUNDAM-UI.pdf"
tell application "Skim" set erRes to (open location aURL with error reporting) end tell |
Skimで現在表示中のPDFで、現在表示中のページ以降を削除
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 |
Skim Notesのじっけん
目下、「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 |
Skim v1.6.8でScripting機能のバグを修正
フリーのPDFビューワー「Skim」の最新版v1.6.8で、前バージョンで発生していたAppleScriptのサポート機能のバグが修正されました。どちらかといえば、OS側の不具合のような雰囲気も漂っています。
SkimのAppleScriptサポート機能にバグ
フリーのPDFビューワー「Skim」の最新版v1.6.7で、AppleScriptのサポート機能にバグが発生していることが明らかになりました。
スクリプトエディタでtell app “Skim”….end tellと入力して構文確認しただけで「内部の表があふれました」というワーニングが出てしまいます。
Script Debuggerでも同様です。
Script MenuからAppleScriptを呼び出して実行する分にはエラーが出ないので、今日もSkim経由でオープン中のJPEG画像を書き出したりしていたのですが、、、、
12/15の時点で、SkimのForum上でこの問題について話題になり、対策ビルドなども出ているようなので、年明けほどなくして解決されることを期待しています。
AppleのKeynote/Pages/Numbersだとあからさまな不具合であっても修正に半年ぐらいかかるので、このあたりはオープンソースのソフトウェアのほうが安心感があります。
最前面のPDFで連続して同じページが存在するかチェック
昨日販売開始した「Cocoa Scripting Course #1」で同じページが連続している箇所をみつけてしまいました。近日中に修正しますが、これを自動でチェックするAppleScriptを書いておく必要性を感じ、作っておきました。
オープンソースのPDFビューワー「Skim」でオープン中のPDFの全ページのテキストを取得し、取得したあとで連続するページのテキスト同士を比較してチェックしています。
必要に応じて各ページを画像にレンダリングして比較する必要があるかと思っていたのですが、テキストだけでもけっこう検出できているのでこんな感じでしょうか。
このページは、AppleScriptにより実際のファイルを検出して表の内容を自動更新していたのですが、その作業バックアップのためにページそのものを複製して誤操作というか作業のやり直しに備えていたのですが、それを削除し忘れたかっこうです。
AppleScript名:最前面のPDFで連続して同じページが存在するかチェック.scptd |
— – Created by: Takaaki Naganoya – Created on: 2021/03/13 — – Copyright © 2021 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use framework "Quartz" use scripting additions script pdfStore property aList : {} end script set (aList of pdfStore) to {} tell application "Skim" set dCount to count every document if dCount = 0 then return tell front document set myPath to path set pCount to count every page end tell end tell set anNSURL to (current application’s |NSURL|’s fileURLWithPath:myPath) set theDoc to current application’s PDFDocument’s alloc()’s initWithURL:anNSURL repeat with i from 0 to (pCount – 1) set aPage to (theDoc’s pageAtIndex:i) set tmpStr to (aPage’s |string|()) set the end of (aList of pdfStore) to (tmpStr as string) end repeat set resList to {} repeat with i from 1 to (pCount – 1) set aText1 to contents of item i of (aList of pdfStore) set aText2 to contents of item (i + 1) of (aList of pdfStore) if aText1 = aText2 then set the end of resList to {i, i + 1} end if end repeat if resList is equal to {} then display notification "PDFに重複ページは見られませんでした。" else set aText to listToText(resList) of me set the clipboard to aText display dialog "重複ページ:" default answer aText with icon 1 buttons {"OK"} default button 1 end if on listToText(aList) set listText to {"{"} set quotChar to ASCII character 34 set firstFlag to true repeat with i in aList set j to contents of i set aClass to class of i if (aClass = integer) or (aClass = number) or (aClass = real) then set the end of listText to (getFirst(firstFlag) of me & j as text) set firstFlag to false else if (aClass = string) or (aClass = text) or (aClass = Unicode text) then set the end of listText to ((getFirst(firstFlag) of me & quotChar & j as text) & quotChar) set firstFlag to false else if aClass is list then set the end of listText to (getFirst(firstFlag) of me & listToText(j)) –ちょっと再帰処理 set firstFlag to false end if end repeat set the end of listText to "}" set listText to listText as text return listText end listToText on getFirst(aFlag) if aFlag = true then return "" if aFlag = false then return "," end getFirst |
Skimでオープン中のPDFで選択中のテキストを返す
フリーでScriptableなmacOS用PDFビューワー「Skim」上でオープン中のPDFで、選択中のテキストの内容を取得するAppleScriptです。
フリーかつオープンソースで提供されているアプリケーションのうち、奇跡的に豊富なAppleScript対応機能を備えるPDFビューワー、それがSkimです。Skimに比べればPreview.appなど取るに足らない存在。「PDFビューワー四天王」のうち、その最上位に君臨するアプリケーションこそがSkimです(四天王とかいいつつ、Skim、Preview、Acrobatの3人しかいないのはお約束)。
▲SkimでPDFをオープンし、「AppleScriptってなんだろう?」の文字列を選択
ただ、そんなグレートな存在のSkimでも、「選択中のテキストを取得する」という処理を書いたことはありませんでした。
Skimの「selection」によって取得されるのがテキストではなくRTFなので、AppleScriptの基本的な機能ではこのRTFはひどく扱いが難しいデータ「でした」。
しかし、Cocoaの機能を利用することで、テキストへの変換は可能です。
それでも、Cocoaが期待するRTFのデータとAppleScriptの世界のRTFのデータ同士の変換が難儀でした。食後の腹ごなしに行うには手に余るといったレベル。
そこで、お気軽データ変換の最後の砦であるクリップボードを経由してRTFをAppleScriptの世界からCocoaの世界に受け渡してみたところ、大成功。あとは、PDFから取得したテキストデータによくあることですが、日本語のテキストだとUnicodeのNormalize方法の問題によりひらがな/カタカナと濁点や半濁点が分離した状態で返ってきました。
これについても、Cocoaの機能を利用してNormalizeを行い、常識的なテキストに変換できました。
AppleScript名:Skimでオープン中のPDFの選択中のテキストを返す |
— – Created by: Takaaki Naganoya – Created on: 2019/09/16 — – Copyright © 2019 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" use scripting additions use framework "Foundation" tell application "Skim" tell front document set aSel to selection repeat with i in aSel set aCon to contents of i set rList to RTF of aCon set sCon to "" repeat with ii in rList set the clipboard to ii set aText to getClipboardAsText() of me set aCon to aCon & aText end repeat set aStr to textfy(aCon) of me return aStr end repeat end tell end tell –Normalize Unicode Text in NFKC on textfy(aText as string) set aStr to current application’s NSString’s stringWithString:aText set aNFKC to aStr’s precomposedStringWithCompatibilityMapping() return aNFKC as string end textfy –Clipboard内の情報をテキストとして取得する on getClipboardAsText() — get the pasteboard items set theClip to current application’s NSPasteboard’s generalPasteboard() set pbItems to theClip’s pasteboardItems() set theStrings to {} repeat with anItem in pbItems if (anItem’s types()’s containsObject:(current application’s NSPasteboardTypeString)) then set end of theStrings to (anItem’s stringForType:(current application’s NSPasteboardTypeString)) as text end if end repeat return theStrings as text end getClipboardAsText |
とか言ってたら、夕飯の買い物に出かけようとした頃にShane Stanleyから「もっとシンプルに書けるよー」というサンプルが届いて脱力しました。もっと簡潔に書けたようです(同一サンプルで日本語データに対してチェックずみ)。
ただ、PDFから文字取り出ししたあとは、Unicodeの再Normalizeは割とやらないといけないケースが多いので、選択部分(selection)からRTFじゃなくてcharacterでデータを取り出せばよかったというあたりが反省点でしょうか。
set theText to ""
tell application "Skim"
tell front document
set aSel to selection
repeat with anItem in aSel
set theText to theText & (characters of anItem) as text
end repeat
end tell
end tell
return theText
CotEditorで編集中のMarkdown書類をPDFプレビュー
CotEditorで編集中のMarkdown書類を、MacDownでPDF書き出しして、Skimでオープンして表示するAppleScriptです。
CotEditorにMarkdownのプレビュー機能がついたらいいと思っている人は多いようですが、MarkdownはMarkdownで、方言は多いし標準がないし、1枚もののMarkdown書類だけ編集できればいいのか、本などのプロジェクト単位で編集とか、目次が作成できないとダメとか、リンクした画像の扱いをどうするのかとか、対応しようとすると「ほぼ別のソフトを作るのと同じ」ぐらい手間がかかりそうです(メンテナー様ご本人談)。
そこで、AppleScript経由で他のソフトを連携させてPDFプレビューさせてみました。これなら、誰にも迷惑をかけずに、今日この時点からすぐにMarkdownのプレビューが行えます(当然、HTML書き出ししてSafariでプレビューするバージョンははるかかなた昔に作ってあります)。
ただし、OS側の機能制限の問題で、CotEditor上のスクリプトメニューから実行はできません(GUI Scriptingの実行が許可されない)。OS側のスクリプトメニューに登録して実行する必要があります。
GUI Scriptingを利用してメニュー操作を行なっているため、システム環境設定で許可しておく必要があります。
本来であれば、PDFの書き出し先フォルダ(この場合は書き出しダイアログで、GUI Scirptingを用いてCommand-Dで指定して一律に場所指定が行えるデスクトップフォルダ)に同名のPDFが存在しないかどうかチェックし、存在すれば削除するといった処理が必要ですが、面倒だったのであらかじめMarkdown書類をUUIDにリネームしておくことで、書き出されたPDFも同じくUUIDのファイル名になるため、論理上はファイル名の衝突を回避できるため、削除処理を省略しています。
AppleScript名:🌏レンダリングしてPDFプレビュー |
— Created 2019-06-15 by Takaaki Naganoya — 2019 Piyomaru Software use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" property NSUUID : a reference to current application’s NSUUID property NSWorkspace : a reference to current application’s NSWorkspace –オープン中のMarkdown書類を取得する tell application "CotEditor" tell front document set cStyle to coloring style if cStyle is not equal to "Markdown" then display dialog "編集中のファイルはMarkdown書類ではないようです。" buttons {"OK"} default button 1 return end if set aPath to path end tell end tell –一時フォルダにMarkdown書類をコピー set sPath to (path to temporary items) tell application "Finder" set sRes to (duplicate ((POSIX file aPath) as alias) to folder sPath with replacing) end tell –コピーしたMarkdown書類をリネーム set s1Res to sRes as alias set aUUID to NSUUID’s UUID()’s UUIDString() as text –UUIDを作成する tell application "Finder" set name of s1Res to (aUUID & ".md") end tell –Markdown書類をデスクトップにPDF書き出し set pdfRes to exportFromMacDown(POSIX path of s1Res) of me –PDF Viewerでオープン tell application "Skim" –Preview.appでもOK activate open pdfRes end tell –一時フォルダに書き出したMarkdown書類を削除 tell application "Finder" delete s1Res end tell –指定のMacDownファイル(alias)をデスクトップ上にPDFで書き出し on exportFromMacDown(anAlias) set s1Text to paragraphs of (do shell script "ls ~/Desktop/*.pdf") –pdf書き出し前のファイル一覧 tell application "MacDown" open {anAlias} end tell macDownForceSave() of me tell application "MacDown" close every document without saving end tell do shell script "sync" –ねんのため set s2Text to paragraphs of (do shell script "ls ~/Desktop/*.pdf") –pdf書き出し後のファイル一覧 set dRes to getDiffBetweenLists(s1Text, s2Text) of me –デスクトップ上のPDFファイル名一覧の差分を取得 set d2Res to (addItems of dRes) if length of d2Res ≥ 1 then return contents of first item of d2Res else error "Error in exporting PDF to desktop folder…." end if end exportFromMacDown on getDiffBetweenLists(aArray as list, bArray as list) set allSet to current application’s NSMutableSet’s setWithArray:aArray allSet’s addObjectsFromArray:bArray –重複する要素のみ抜き出す set duplicateSet to current application’s NSMutableSet’s setWithArray:aArray duplicateSet’s intersectSet:(current application’s NSSet’s setWithArray:bArray) –重複部分を削除する allSet’s minusSet:duplicateSet set resArray to (allSet’s allObjects()) as list set aSet to current application’s NSMutableSet’s setWithArray:aArray set bSet to current application’s NSMutableSet’s setWithArray:resArray aSet’s intersectSet:bSet –積集合 set addRes to aSet’s allObjects() as list set cSet to current application’s NSMutableSet’s setWithArray:bArray cSet’s intersectSet:bSet –積集合 set minusRes to cSet’s allObjects() as list return {addItems:minusRes, minusItems:addRes} end getDiffBetweenLists –注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え on macDownForceSave() activate application "MacDown" tell application "System Events" tell process "MacDown" — File > Export > PDF click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1 –Go to Desktop Folder keystroke "d" using {command down} –Save Button on Sheet click button 1 of sheet 1 of window 1 end tell end tell end macDownForceSave –Bundle IDからアプリケーションのPathを返す on retAppAbusolutePathFromBundleID(aBundleID) set appPath to NSWorkspace’s sharedWorkspace()’s absolutePathForAppBundleWithIdentifier:aBundleID if appPath = missing value then return false return appPath as string end retAppAbusolutePathFromBundleID |
Skimで表示中のページ数を現在のNumbersのセルに入れて、選択セルを下にひとつ移動
Skimで現在表示中のPDFの現在のページ番号を、Numbers書類で選択中のセルに入れて、Numbersの選択中のセルを1つ下に移動させるAppleScriptです。
作業をちょっとだけ楽にするために作成したAppleScriptです。本来は、すべてのワークフローを自動化するのが筋ですが、限られた時間で限られた範囲の作業のみを自動化するだけでも十分な効果が得られる場合があります。
複数のアプリケーションをまたいでデータのコピー&ペーストを行うようなケースだと、ちょっとした複合作業を自動化することで作業が圧倒的に楽になります。
PDFビューワー「Skim」上でPDFを表示、Numbers上の表にページ数のデータを入れたい。
本AppleScriptを実行すると、
Numbersの選択セルにデータを突っ込んで、選択セルを1つ下に移動させます。
こうして、PDFにTOCをつけてみました(Blogアーカイブ書籍のマイナーバージョンアップ版のため。再ダウンロードでアップデート可能になります)。元データがあるんだから、TOCごと自動で作ってもいいようなものですが、久しぶりだったので(TOC用データを)手で作ってみました。
ここで、意地でもGUI Scriptingを使わないで処理すると、バックグラウンドでも安全に処理できるので大きなメリットがあります。GUI Scriptingだと最前面(フォアグラウンド)でしか操作が保証されていないので、バックグラウンドで操作できたとしてもそれは「ラッキー」でしかありません。
AppleScript名:Skimで表示中のページ数を現在のNumbersのセルに入れて、選択セルを下にひとつ移動 |
— Created 2018-08-18 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" tell application "Skim" tell front document set curPage to index of current page end tell end tell set curPageStr to curPage as string tell application "Numbers" tell front document tell active sheet tell table 1 set mySelectedRanges to properties of selection range set value of cells of selection range to {curPageStr} set selName to name of selection range –選択範囲のrange情報を取得 set {s1, s2} to parseByDelim(selName, ":") of me set s1Row to (address of row of range s1) as integer set colStr to (name of column of range s1) as string set rowStr to (s1Row + 1) as string set adrStr to colStr & rowStr set rangeStr to adrStr & ":" & adrStr try set selection range to range rangeStr on error –Range Over Error from table 1 display dialog "表の範囲をオーバーします" return end try end tell end tell end tell end tell –テキストを指定デリミタでリスト化 on parseByDelim(aData, aDelim) set aText to current application’s NSString’s stringWithString:aData set aList to aText’s componentsSeparatedByString:aDelim return aList as list end parseByDelim |
SkimでPDF表示モードを「単一」と「2ページ見開き」でトグル切り替え
オープンソースのPDFビューワー「Skim」でPDF表示モードを「単一ページ」と「2ページ見開き」でトグル切り替えするAppleScriptです。
▲Skim 単一ページ表示モード
▲Skim 2ページ見開きモード
一応、macOS標準搭載のScript Menuから呼び出して実行できることを確認していますが、、、
正直なところ、ウィンドウ上のボタンを操作したほうがScript Menuを操作するよりも手っ取り早いので、本Scriptに実用性はほとんどありません。
単に、SkimのPDF表示モードの指定方法を調べたので、その方法をScriptで記述しておいた(スケッチ)ぐらいの意味合いです。
AppleScript名:SkimでPDF表示モードを「単一」と「2ページ見開き」でトグル切り替え |
tell application "Skim" tell front document set curViewSets to view settings –> {auto scales:true, displays as book:false, displays page breaks:true, scale factor:1.235772357724, display mode:two up, display box:crop box} set curMode to display mode of curViewSets if curMode = single page then set display mode of curViewSets to two up else set display mode of curViewSets to single page end if set view settings to curViewSets end tell end tell |
Skimで現在表示中のPDFのページをJPEG画像で書き出す
オープンソースのPDFビューワー「Skim」で現在表示中のPDFの現在表示中のページをJPEG画像で書き出すAppleScriptです。
macOS標準装備のPreview.appはつい最近AppleScriptからの制御が公式に行えるようになった程度で、対応度も最低限です。Skimであれば、こうした「現在オープン中のPDFの表示中のページ」の番号を取得することもできます。
macOS標準装備のScript Menuから呼び出して利用することを前提に作成してあります。
Skimでオープン中の最前面のPDFと同じフォルダに、元ファイル名に対してページの数値をゼロパディングして追加したものをJPEG画像のファイル名に指定しています。ファイル名の重複回避などは行っていません。
ただし、Skimで表示モードが「単一」(1ページ分のみビューワーに表示)以外になっていると、カレントページ(現在表示中のページ)が画面上とアプリケーション側で管理しているものがズレることがあるので、表示モードを「単一」に切り替えてから「このページでよいか?」といった確認をユーザーに対して行うとよいかもしれません。
AppleScript名:Skimで現在表示中のPDFのページをJPEG画像で書き出す |
— Created 2018-06-30 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "Quartz" use framework "AppKit" property |NSURL| : a reference to current application’s |NSURL| 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 NSNumber : a reference to current application’s NSNumber property NSZeroPoint : a reference to current application’s NSZeroPoint property PDFDocument : a reference to current application’s PDFDocument property NSJPEGFileType : a reference to current application’s NSJPEGFileType property NSCompositeCopy : a reference to current application’s NSCompositeCopy property NSGraphicsContext : a reference to current application’s NSGraphicsContext property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep property NSNumberFormatter : a reference to current application’s NSNumberFormatter property NSImageInterpolationHigh : a reference to current application’s NSImageInterpolationHigh 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 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 compFactor to 1.0 –1.0 — 0.0 = max jpeg compression, 1.0 = none –Detect Retina Environment set retinaF to NSScreen’s mainScreen()’s backingScaleFactor() if retinaF = 1.0 then set aScale to 2.0 –Non Retina Env else set aScale to 1.0 –Retina Env end if –PDFをページごとに分割してJPEGでファイル書き出し set i to (curInd – 1) –AppleScript index (1 based) to Cocoa index (0 based) conversion –Pick Up a PDF page as an image set thisPage to (aPDFdoc’s pageAtIndex:(i)) set thisDoc to (NSImage’s alloc()’s initWithData:(thisPage’s dataRepresentation())) if thisDoc = missing value then error "Error in getting imagerep from PDF in page:" & (i as string) –Resize Image set pointSize to thisDoc’s |size|() set newSize to current application’s NSMakeSize((pointSize’s width) * aScale, (pointSize’s height) * aScale) set newImage to (NSImage’s alloc()’s initWithSize:newSize) newImage’s lockFocus() (thisDoc’s setSize:newSize) (NSGraphicsContext’s currentContext()’s setImageInterpolation:(NSImageInterpolationHigh)) (thisDoc’s drawAtPoint:(NSZeroPoint) fromRect:(current application’s CGRectMake(0, 0, newSize’s width, newSize’s height)) operation:(NSCompositeCopy) fraction:2.0) newImage’s unlockFocus() –Save Image as JPEG set theData to newImage’s TIFFRepresentation() set newRep to (NSBitmapImageRep’s imageRepWithData:theData) set targData to (newRep’s representationUsingType:(NSJPEGFileType) |properties|:{NSImageCompressionFactor:compFactor, NSImageProgressive:false}) set zText to retZeroPaddingText((i + 1), 4) of me set outPath to addString_beforeExtensionIn_addingExtension_("_" & zText, aPOSIX, "jpg") ( targData’s writeToFile:outPath atomically:true) –書き出し –ファイルパス(POSIX path)に対して、文字列(枝番)を追加。任意の拡張子を追加on addString:extraString beforeExtensionIn:aPath addingExtension:aExt set pathString to NSString’s stringWithString:aPath set theExtension to pathString’s pathExtension() set thePathNoExt to pathString’s stringByDeletingPathExtension() set newPath to (thePathNoExt’s stringByAppendingString:extraString)’s stringByAppendingPathExtension:aExt return newPath as string end addString:beforeExtensionIn:addingExtension: on retZeroPaddingText(aNum as integer, aDigitNum as integer) if aNum > (((10 ^ aDigitNum) as integer) – 1) then return "" –Range Check set aFormatter to NSNumberFormatter’s alloc()’s init() aFormatter’s setUsesGroupingSeparator:false aFormatter’s setAllowsFloats:false aFormatter’s setMaximumIntegerDigits:aDigitNum aFormatter’s setMinimumIntegerDigits:aDigitNum aFormatter’s setPaddingCharacter:"0" set aStr to aFormatter’s stringFromNumber:(NSNumber’s numberWithFloat:aNum) return aStr as string end retZeroPaddingText |