メモ
PDFファイルの しおり のレベルが9以下の部分を対象にして、PowerShellでしおり名としおり移動先ページ番号をCSV形式でクリップボードにコピーする。
参考資料リンク
使い捨てスクリプト
- エラー処理やCOMオブジェクトの解放を気にしていないので練習以外で実行しない
- Acrobatのプロセスを停止させるので事前にAcrobatでの作業を終了してください
- しおりのアクションに「JavaScriptを実行」や「別の文書内のページに移動」は無いものとします
# [テスト環境] # PowerShell 5.1, Acrobat Standard DC (2022年11月頃), Windows 11 # [ファイル名] # "%USERPROFILE%\Desktop\GetPdfBookmark.ps1" # [エンコード] # UTF-8 (BOM付き) # [ショートカット(.lnk)] # リンク先(T): # PowerShell.exe -NoLogo -NoExit -NoProfile -ExecutionPolicy RemoteSigned -File "%USERPROFILE%\Desktop\GetPdfBookmark.ps1" # 作業フォルダー(S): # "" using namespace Microsoft.VisualBasic using namespace System.Collections.Generic using namespace System.Runtime.InteropServices Set-StrictMode -Version Latest # CreateObjectとCallByNameを使えるようにする Add-Type -AssemblyName Microsoft.VisualBasic # Write-Verboseの結果を表示する $VerbosePreference = "Continue" if ($args.Length -eq 0) { Write-Verbose "ショートカット(.lnk)にPDFファイルをドラッグ&ドロップしてください" throw '$args.Length -eq 0' } # パスに角括弧[]を含む場合の作業フォルダー対応 Set-Location -LiteralPath (Split-Path -LiteralPath $args[0]) Write-Verbose "Acrobatでの作業を終了してください" Write-Verbose "しおり のレベルが9を超える部分は無視します" timeout /t 3 # 再帰関数 function Get-BookmarkName { param( [object]$jso, [object]$bkm, [List[PSCustomObject]]$docBookmarks, [int]$lv, [string]$lvStr ) if ($lv -gt 9) { Write-Verbose "しおり のレベルが9を超える箇所が存在します" return } try { $bkms = [Interaction]::CallByName($bkm, "children", [CallType]::Get) } catch { return } for ($i = 0; $i -lt $bkms.Length; $i++) { Get-BookmarkName ` $jso ` $bkms[$i] ` $docBookmarks ` ($lv + 1) ` ($lvStr + "{0:00}-" -f ($i + 1)) # しおりを実行 [Interaction]::CallByName($bkms[$i], "execute", [CallType]::Method) # ファイル名(Doc.documentFileName)を取得 $fName = [Interaction]::CallByName($jso, "documentFileName", [CallType]::Get) # 移動後のページ番号(Doc.pageNum)を取得 $page = [int][Interaction]::CallByName($jso, "pageNum", [CallType]::Get) # しおりの名前(Bookmark.name)を取得 $bName = [Interaction]::CallByName($bkms[$i], "name", [CallType]::Get) # 先頭ページに移動 [Interaction]::CallByName($jso, "pageNum", [CallType]::Let, 0) # Format演算子 $lvStrTmp = $lvStr + "{0:00}" -f ($i + 1) # ソートしやすいように文字数を合わせる for ($j = $lv; $j -lt 9; $j++) { $lvStrTmp += "-00" } $docBookmarks.Add([PSCustomObject]@{ FileName = $fName; LevelStr = $lvStrTmp; Level = $lv; ItemNum = $i + 1; Page = $page + 1; Name1 = $null; Name2 = $null; Name3 = $null; Name4 = $null; Name5 = $null; Name6 = $null; Name7 = $null; Name8 = $null; Name9 = $null }) # インデックスに-1を使用して最後の要素を取得 $docBookmarks[-1].("Name" + $lv) = $bName } for ($i = 0; $i -lt $bkms.Length; $i++) { [Marshal]::FinalReleaseComObject($bkms[$i]) > $null } } # しおりの名前や移動先ページ番号などを保存 $docBookmarks = [List[PSCustomObject]]::new() # AVDocオブジェクトを作成 $avDoc = [Interaction]::CreateObject("AcroExch.AVDoc") for ($i = 0; $i -lt $args.Length; $i++) { # PDFファイルのパス $pdfFilePath = $args[$i] # PDFファイルを開く(AVDoc) $ret = $avDoc.Open($pdfFilePath, "") Write-Verbose "`$avDoc.Open(`"$pdfFilePath`", `"`") ..... $ret" # PDDocを取得 $pdDoc = $avDoc.GetPDDoc() # JavaScript object を取得 $jso = $pdDoc.GetJSObject() # bookmarkRootを取得 $bkmR = [Interaction]::CallByName($jso, "bookmarkRoot", [CallType]::Get) Get-BookmarkName ` $jso ` $bkmR ` $docBookmarks ` 1 ` "" [Marshal]::FinalReleaseComObject($bkmR) > $null [Marshal]::FinalReleaseComObject($jso) > $null # PDFファイルを閉じる $pdDoc.Close() > $null [Marshal]::FinalReleaseComObject($pdDoc) > $null # PDFファイルを閉じる(AVDoc) $ret = $avDoc.Close($true) Write-Verbose "`$avDoc.Close(`$true) ..... $ret" } [Marshal]::FinalReleaseComObject($avDoc) > $null if ($docBookmarks.Count -ne 0) { # PSCustomObjectのリストをソート $docBookmarks = $docBookmarks | Sort-Object ` -Property ` @{Expression = "FileName"; Descending = $false}, @{Expression = "LevelStr"; Descending = $false} # PSCustomObjectの配列をCSVに変換 $csv = $docBookmarks | ConvertTo-Csv -NoTypeInformation # CSVをクリップボードにコピー $csv | Set-Clipboard # グリッド ビュー に表示 $docBookmarks | Out-GridView } else { Write-Verbose "しおり がありません" } # ガベージ コレクション を直ちに強制実行 [GC]::Collect() [GC]::WaitForPendingFinalizers() # Acrobatのプロセスを取得 $ps = @(Get-Process | Where-Object Name -match '^Acrobat$|^AcroCEF$') # Acrobatのプロセスを停止 if ($ps.Length -ne 0) { $ps | Stop-Process } Start-Sleep -Seconds 1.5 # Acrobat関係のプロセスを表示 Write-Verbose @" `nGet-Process | Where-Object Name -match 'Acro' $(Get-Process | Where-Object Name -match 'Acro' | Out-String -Width 80) "@