Cocoa AppleScript PDFからテキスト抽出 AppleScript [08]

AppleScript[08](CatchImage)

AppleScript [08] Cocoa AppleScript 第3版 PDFからテキスト抽出
 今まで作成したAppleScript部品を応用して、指定フォルダ(子フォルダを含む)内のPDFファイルを順に読み込み、同一フォルダ内にテキストファイルを書き出す機能を追加!
 「Adobe純正のPDF編集ソフト(Acrobat DC)」は無料でないため、Mac用アプリ「Skim」を利用。
 Mac純正のPDFリーダー「プレビュー」はなぜかAppleScript用のインターフェースがほとんど用意されていないため、無料のPDFリーダー兼注釈付加ツールの「Skim」のインストールが必要!


 (注) AppleScript にて、フォルダ選択ダイアログを利用します!

 2021/10/20  Ver. 3.3「Cocoa AppleScript 第3版」PDFからテキスト抽出ハンドラーの追加
→ AppleScript は "filePath.scpt" ファイルに「1ハンドラーを追加」、VBAコードは「1プロシージャの追加(インターフェース)」となります。


 念のため書いておきますが、PDF内の文字が画像の一部になったままではテキスト抽出できないため その場合はいわゆる「OCR(光学文字認識)」スキャンを行う必要あり!

 【注意】テキストファイルの上書き(置き換え)を行う場合、必ず該当ファイルを閉じてから実行して下さい!!
 テキストエディタ等で閉じるのを忘れた場合(つまり、ファイルをオープンしたままだと)、メモリ不足など Mac が深刻な症状に陥る場合があります。 筆者もテストの際2、3度「やっちまった」次第で、いきなり Safari や Excelが終了してビックリ...

 Skim のことも「ぴよまるソフトウェア」さんのブログ記事(以下リンク)で知りました! AppleScriptの最新情報を入手可能なブログ(ただし、中級者以上向け)です。






 【AppleScriptの追加】 AppleScript [08] Cocoa AppleScript 第3版 「PDFからテキスト抽出」ハンドラーの追加 

 まず、Excel メニューバーの [ヘルプ] - [更新プログラムのチェック] にて、適用されていないアップデートがあれば行ってください。


 無料のPDFリーダー兼注釈付加アプリ「Skim」のインストール 

AppleScript[08] Png01

 「Adobe純正のPDF編集ソフト(Acrobat DC)」は無料でないため、Mac用アプリ「Skim」を利用。
 Mac純正のPDFリーダー「プレビュー」はなぜかAppleScript用のインターフェースがほとんど用意されていないため、無料のPDFリーダー兼注釈付加ツールの「Skimアプリ」のインストールが必要!
 AppleはPDFを取り扱うツールとして「Automator」アプリを推奨しているようでこちらはPDF関連のインターフェースが複数用意されていますが、「Automator」はiPhone/iPadと同様の「ショートカット」アプリに徐々に移行予定だそうです。 なお、「PDFからテキスト抽出」1ファイルのみであれば「Automatorのワークフロー」で簡単にでき、特にRTF(RichTextFormat)ファイルとして書き出すのであれば一択です。 AppleScriptで「Skim」を利用してRTF書き出し(拡張子 .rtf )すると何故か Mac純正の「テキストエディット」アプリで開けません。 MS Wordで(UTF-8かつ改行コードを指定して)開いてもテキスト(拡張子 .txt つまり、PlainText)書き出しとほぼ同等の結果しか得られないため、UTI指定(明示的に public.rtf 指定していない)だけの問題ではなさそうです。
 「Skim」に問題があるのではなく、AppleScript で RTF を取り扱うには もうひと手間 必要なのかもしれません!

AppleScript[08] Png02

 Skimアプリのインストールは  こちらから! 
「緑色のDownloadボタン」から 2021/10/22現在 Skim-1.6.5.dmg ファイルをダウンロード可能で、ダブルクリックして開くと以下のフォルダが開くため フォルダ内の「Skim.app」のアイコンをアプリケーションフォルダ内に ドラッグ&ドロップすると インストール開始! (ほとんどの場合、数秒でインストールが完了するため)前述のフォルダを閉じた後、(通常、デスクトップに追加される)アイコンを右クリックして「"Skim"を取り出す」を選択しマウント解除すると 拡張子 dmg を持つファイルが削除可能に...(画面添付あり)
 「Skimアプリ」はデフォルト状態のままで AppleScript から利用可能ですが、初回起動時(AppStore経由でないため)「確認メッセージ」が表示される。(画面添付あり) 「Skim.app」をダブルクリック後、「開く」ボタンを選択して「Skimアプリ」を起動したら、そのままアプリを終了しても良い。


AppleScript[08] Png03

AppleScript[08] Png04

AppleScript[08] Png05


 VBA の AppleScriptTask コマンド パラメータの注意点 

  ファイルパスと区別するため フォルダパス指定には 末尾に必ず / を付加 してください! フォルダフルパス なら 先頭と末尾が必ず / となります。
 OneDriveiCloudDropboxBox 等のサービスをブラウザ利用でなくフォルダにリンクしている場合も(リンク)パスが判れば指定可能ですが、https:// が先頭に付いたり ネットワーク経由のアクセスのためとんでも無く時間がかかりますので ローカルPC(Mac本体内の高速なSSDやHDDドライブ)上のフォルダパスを指定してください。


 第3版 Cocoa AppleScript コードへ ハンドラーを1つ追加 

(1)  以前に  作成したExcelファイルをマクロを有効にして開き、続いて VBEウィンドウ を開く。

(2) ~/ライブラリ/Application Scripts/com.microsoft.Excel/ フォルダ内に保存した filePath.scpt をダブルクリック等で("スクリプトエディタ"アプリにて)開き、 ファイル内の全AppleScriptコードを以下のコードで置換し、 コンパイル後に ファイルを保存 したら "スクリプトエディタ"アプリは終了してください。

 上記の操作方法がよく解らない方は、  以前の投稿  をお読みください。

 VBA、AppleScript 両方のコードに コメントをたくさん記述しておいたので、宜しければ ご覧ください!

 2021/10/20  Ver. 3.3「Cocoa AppleScript 第3版」PDFからテキスト抽出ハンドラーの追加
→ AppleScript は "filePath.scpt" ファイルに「1ハンドラーを追加」、VBAコードは「1プロシージャの追加(インターフェース)」となります。


 今回のハンドラー追加前のAppleScriptコードは、  前回記事  から コピー&ペースト(貼り付け) して下さい!
 ハンドラー1つの追加のため 以下のAppleScriptコードを 末尾に コピー&ペースト(貼り付け)後、 コンパイル後に ファイルを保存 してください。 VBAコード に関しては「次章」に記載します。






--2021/10/20 Ver.3.3 Cocoa AppleScript 対応 第3版 以下ハンドラーを追加
-- 指定フォルダ(子フォルダを含む)内のPDFファイルを順に読み込み、テキストファイルを書き出し
-- パラメータ[1]  プロンプト文字列:""なら、当ハンドラー設定の初期値
-- パラメータ[2]  初期表示フォルダ:""なら、無指定(前回と同じフォルダを開く)
-- パラメータ[3]  書式": txt" or "rtf"(省略時は、"txt":PlainText指定)RTF:RichTextFormat
-- 実行結果[0~n] (発見した)PDFファイルのフルパス、ただし ".pdf" の拡張子は削除済み
--[注意] Macに予め、Skimアプリ(無料のPDFリーダー+注釈付加ツール)のインストールが必要!
on putTextFromPdfOfSelectFolder(paramStr)
  set returnText to ""
  set promptStr to "PDF格納フォルダを選択!(子フォルダ対応)" --  ファイル選択時のプロンプト文字列
  
  try
    set tmp to (AppleScript's text item delimiters) --  現在のデリミタを保存
    set (AppleScript's text item delimiters) to {LF} -- デリミタを変更
    set paramList to (every text item of paramStr) -- 分割し、List格納
    set (AppleScript's text item delimiters) to tmp --  保存したデリミタに戻す
    
    set str to ((first item of paramList) as string)
    if (str ≠ "") then --not=は「/=」と記述
      set promptStr to str
    end if
    set paramList to (rest of paramList) --最初の要素を削除
    set defaultLocation to ((first item of paramList) as string)
    set paramList to (rest of paramList) --最初の要素を削除
    
    if (count paramList) = 0 then
      set isRtf to false
    else
      set rtf to ((first item of paramList) as string)
      if rtf is in {"rtf", "RTF", "Rtf"} then
        set isRtf to true --RTF:RichTextFormat
      else
        set isRtf to false
      end if
    end if
    
    activate --ウィンドウの前面に表示!
    if (defaultLocation = "") then
      set theAlias to (choose folder with prompt promptStr)
    else
      set defaultLocation to (POSIX path of defaultLocation)
      set theAlias to (choose folder with prompt promptStr default location defaultLocation)
    end if --選択したフォルダの「エイリアス値」が返される
    
    tell application "Finder"
      set theFolder to (POSIX path of (theAlias as string)) -- 指定フォルダのPOSIXパス
    end tell
    
    set fileWriteCount to 0 --ファイル書き込み成功 件数
    set theList to {}
    set theList to my retFullPathWithinAFolderWithRecursiveFilterByExt(theFolder, "pdf")
    
    tell application "Skim" --AppleScriptで利用し易い PDF読み込み(注釈付加も可能)アプリ
      activate --"Skim"App
      
      repeat with fName in theList --PDFファイルのフルパス(ただし、末尾の .pdf は削除)
        
        set theDoc to (open (fName & ".pdf")) --Open PDF's file
        
        tell front document --該当PDFファイルのみを対象!(同時に複数Open可能なため)
          if isRtf then --RTF:RichTextFormat
            set textFromPDF to (get text for as rich text) --該当PDFから、全[default:RTF]を抽出
          else
            set textFromPDF to (get text for as text) --該当PDFから、全テキストを抽出
          end if
        end tell --「文字コードと改行コード」は元のPDFファイルのまま、抽出
        
        --[注1] 既に同一ファイルが存在したら、無条件 置き換え
        --[注2] テキストファイル(文字コードUTF-8)の改行コードは、PDFファイルのまま
        if isRtf then --RTF:RichTextFormat
          set theResult to my write_to_text_file_asUTF8(textFromPDF, (fName & ".rtf"), false, LF)
        else
          set theResult to my write_to_text_file_asUTF8(textFromPDF, (fName & ".txt"), false, LF)
        end if
        if theResult then --ファイル書き込み成功の場合
          set fileWriteCount to (fileWriteCount + 1)
        end if
        
        close theDoc --Close PDF's file
        
      end repeat
      
      delay 3.0 -- 無くても、ほとんどの場合 動作するはず
      quit --"Skim"Appの終了
    end tell
    
    activate
    display dialog (fileWriteCount as string) & " 件のテキストファイルを書き込みました!" buttons {"10秒で閉じます!"} with icon caution giving up after 10
    
    set returnText to "" --  ハンドラ-の戻り値を初期化
    set returnText to getTextWithDelimiters(theList, {LF}) --ハンドラ-の戻り値へ 格納
    set the clipboard to {} --クリップボードの初期化
    set the clipboard to returnText --読み込みファイルデータを、クリップボードへ格納(UTF-8)
    
    return returnText
    
  on error
    set returnText to "" --  ハンドラ-の戻り値を初期化
    set (AppleScript's text item delimiters) to tmp --  保存したデリミタに戻す
    display dialog "★ AppleScriptTask (putTextFromPdfOfSelectFolder) ★" & return & return & "   キャンセル あるいは 異常終了" buttons {"10秒で閉じます!"} with icon caution giving up after 10
  end try -- 「キャンセル」ボタン押下時用のエラーハンドリング
  
  return returnText -- display dialog returnText
  
end putTextFromPdfOfSelectFolder

 ごめんなさい。 Cocoa用の(クラス)オブジェクト定義が足りなくて、コンパイルエラーになります。
 AppleScript先頭部分の property定義を 以下で置き換えてください! 
  再度 コンパイル後に ファイルを保存 する必要があります。



--NSObjectを最上位とする以下NSオブジェクトは Cocoa 用のオブジェクトで、Objective-C で実装されている!
property |NSURL| : a reference to current application's |NSURL| --SubDir内を除外したDir内の列挙時に利用
property NSFont : a reference to current application's NSFont
property NSColor : a reference to current application's NSColor
property NSString : a reference to current application's NSString --dirPath, fileName, ext 取得可
property NSPredicate : a reference to current application's NSPredicate --正規表現 Esc処理に一部問題あり
property NSDictionary : a reference to current application's NSDictionary
property NSEnumerator : a reference to current application's NSEnumerator --列挙用
property NSFileManager : a reference to current application's NSFileManager --"Finder" の代替Obj
property NSLiteralSearch : a reference to current application's NSLiteralSearch
property NSMutableArray : a reference to current application's NSMutableArray --変更可能な NSArray
property NSMutableString : a reference to current application's NSMutableString --変更可能な NSString
property NSUTF8StringEncoding : a reference to current application's NSUTF8StringEncoding --UTF-8Encode
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
property NSDirectoryEnumerationSkipsSubdirectoryDescendants : a reference to current application's NSDirectoryEnumerationSkipsSubdirectoryDescendants --Skip SubDir's (File or Dir)
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to current application's NSDirectoryEnumerationSkipsPackageDescendants --Skip Package. ex) "Microsoft Excel.app"
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application's NSDirectoryEnumerationSkipsHiddenFiles --Skip HiddenFiles for Enumeration

 ちなみに、  ret  で始まる AppleScript の ハンドラー (VBAのプロシージャ相当の呼び出し単位)は Cocoa AS で主処理が実装されていますので、カスタマイズは慎重にお願いします。 その他の ハンドラー も Cocoa AS 実装の部分が判るように記述 しています。(何も記述が無ければ、Vannila AS ということ) Vanilla AS 部分はカスタマイズが楽で「 AppleScriptTask コマンド経由の呼び出し」ではなく、「 filePath.scpt をダブルクリック後に起動される スクリプトエディタ.app にて実行」すれば( Vanilla AS 部分の場合 )エラー発生箇所が判ります。 先頭部分の on test() から end test 内にサンプルのデバッグ用コードを記述済みのため、変更後に [command]+[K] でコンパイル、 test() の実行は [command]+[R] で可能です。 動作に問題が無い場合は [command]+[S] にて保存を忘れないように!

 VBA だけでなく AppleScript コードも テキスト保存が可能なため、変更前に [command]+[A] にて全選択後 [command]+[C] でコピー してテキストエディタ等に [command]+[V] にてペースト(貼り付け)しておけば元に戻せます。 カスタマイズ作業前に filePath.scptAppleScriptTaskコマンドを記述したExcelファイル を ローカルPC上か クラウド領域にバックアップしておくと なお良いでしょう。


AppleScript[08] Png08

 【VBA I/F追加】 AppleScript [08] Cocoa AppleScript 第3版 「PDFからテキスト抽出」プロシージャの追加 

 VBAコード の追加のため、末尾に 以降のコードをコピー&ペースト(貼り付け) 


' 指定フォルダ(子フォルダを含む)内のPDFファイルを順に読み込み、テキストファイルとして書き出し
' [注意] 予め、Skimアプリ(無料のPDFリーダー+注釈付加ツール)のインストールが必要!
Sub WriteTextFileFromPdfOfFolder()
'フォルダ選択ダイアログで指定すると、同一フォルダ内に「拡張子のみ.txtに変更」して書き込み
    On Error GoTo myError
    
'以下は、AppleScriptTaskコマンドのパラメータ説明
' パラメータ[1]  プロンプト文字列:""なら、AppleScriptハンドラー設定の初期値
' パラメータ[2]  初期表示フォルダ:""なら、無指定(前回と同じフォルダを開く)
' パラメータ[3]  書式": txt" or "rtf"(省略時は、"txt"PlainText指定)RTF:RichTextFormat
' 実行結果[0〜n] エラー時は""、 正常終了なら(拡張子なし)該当PDFファイルのフルパスを返す
    Dim list() As String '          「フルパス」格納用として「文字列の配列」を定義
    Dim scriptResult As String '    AppleScript 実行結果  (文字列)
    Dim scriptParam As String '     AppleScript パラメータ(文字列)
    'scriptParam = "Select PDF's folder!" & vbLf & "/Users/username/Desktop/"
    scriptParam = "" & vbLf & "" & vbLf & ""
    scriptResult = AppleScriptTask("filePath.scpt", "putTextFromPdfOfSelectFolder", scriptParam)
    
    If scriptResult <> "" Then   '「キャンセル」ボタン押下時や、PDFファイル無し→空文字列
        list = Split(scriptResult, vbLf) 'vbLf = Chr(10) 区切り文字に LF を利用
        Call DispArray(list) '実行結果(文字列)の表示
        Erase list '配列の初期化
        Call GetClipBoard 'クリップボードに格納したテキストデータを取得&表示
    End If

    Exit Sub
    
myError:
    MsgBox "エラー発生!(WriteTextFileFromPdfOfFolder)"
End Sub

 前述の通り、インターフェースとして rtf(RichTextFormat)ファイルの書き出し も指定可能ですが 出力rtfファイルを Mac純正の「テキストエディット」アプリで開けないため、「拡張子 .txt の PlainText」ファイルの書き出しでご利用ください!
( パラメータ[3] 省略時も "txt" 指定 )

(1) 「 イミディエイト ウィンドウ から call WriteTextFileFromPdfOfFolder() 」等で実行すると まず フォルダ選択ダイアログ が開くため、PDFファイルを格納したフォルダを指定!
(2) 子フォルダ対応で、「 拡張子 pdf 」を持つファイルのフルパス(処理し易いよう、末尾の .pdf は削除)のリストを作成
(3) 「Skimアプリ」で (2) のリストを順に処理。 PDFファイルをオープン後 各PDF内のテキストを抽出し、(2) のパスの末尾に .txt あるいは .rtf を付加して 同一フォルダ内に テキストファイルとして書き込み。 (同一ファイル名が存在した場合、無条件 ファイルを置き換え)
(4) 正常終了すると、処理件数をダイアログ表示(10秒で自動的に閉じる)

 処理結果として、結果文字列クリップボード に (2) のリスト(配列)をセット!

AppleScript[08] Png06

AppleScript[08] Png07

 今まで作成した AppleScript 部品を組み合わせて、「PDFからテキスト抽出」機能を付加しました! 部品の再利用化が進むと AppleScript および VBA の両方で少ないコードで機能付加が可能となるため、 再利用し易いように部品を設計するのも重要です。 この辺りは経験を重ねるしかないと思いますので、 VBA や AppleScript に限らず プログラムコードをたくさん書けば 誰でもできるようになるはず...

 Mac の VBA のみで実現出来ないことをまた見つけたら、 AppleScript で補完したいと思います! macOS12以降で追加される「ショートカット」アプリとの連携方法も理解できたら、記事にまとめないと...




 [command] + [Q] にてまず VBE を終了し、Excel のウィンドウ内を選択して [command] + [Q] にて Excel も終了させます。 「作業中のブック」を保存するか確認のダイアログ画面が表示された場合は 「保存」しましょう。 「自動保存」済みの場合、保存するかどうかの確認ダイアログ画面は表示されません。


 AppleScriptTask コマンドの結果文字列(エラーがあれば、空文字 "" を返す)だけでなく、クリップボードにも同じ内容(改行コードは異なる場合あり)をセットしているため、必要があれば「片方のみ」に変更してください。 ただし、「フルパスやファイル名以外」を返す結果文字列の場合 クリップボードにはセットしないため、「イミディエイト ウィンドウ」にて確認してください!
 AppleScript から VBA に数百ファイル以上結果が返されるのであれば、大量データが渡されるオーバーヘッドも含め、クリップボードのみの利用(結果文字列は、正常終了/異常終了のみ判れば良い)がオススメです。 また、バックグラウンド処理前提の場合は 相互で クリップボードの中身を消してしまう可能性 があるため、結果文字列のみの利用が良いでしょう。

 1日2時間くらい2ヶ月 AppleScript を勉強した成果として、今回の記事を書きました。 Cocoa AppleScript に関しては初心者のため、不具合があればコメント等で教えて頂けると嬉しいです。








Macブログ ランキング アイコン
最後まで読んでいただき、ありがとうございます。 また、お越しくださいませ。
// アタル
For follow LINE Reader Group!Subscribe to this blog on Feedly!

Next Post Previous Post
1 Comments
  • アタル
    アタル 2021年11月4日 21:04

    --「スクリプトエディタ.app」にて以下のAppleScriptを実行すると、指定PDFファイルを TXTファイルに変換します! (PlainText)
    --予め、"Skim.app" インストール要

    ------------------------------------------------------------
    --Copyright 2021- ataruchi. [ https://twitter.com/ataruchi ]
    use AppleScript version "2.4"
    use scripting additions
    use framework "Foundation"
    use framework "AppKit"

    tell application "Skim"

    activate --"Skim.app" を最前面で表示

    --オープンしたPDFファイルのフルパスをセット
    set pdfFile to (open (POSIX path of (choose file of type {"com.adobe.pdf"})))

    tell front document --pdfFile

    set txtData to (get text for as text) --Plain Text 情報を取得

    close pdfFile

    end tell

    quit --"Skim.app" を終了

    end tell

    activate --「Save File」ダイアログを最前面で表示
    set txtFile to POSIX path of (choose file name default name "test.txt") --出力TXTファイルパス

    -- write the string out as a TXT file(無条件、置換)
    --PDFファイルの「改行コード」のまま、テキストファイル書き込み!
    --AppleScript文字列を Cocoa文字列に変換
    set theNSString to (current application's NSString's stringWithString:txtData)

    --「結果」テキストファイル出力に成功したら true 、失敗なら false が返る [UTF-8 指定]
    return (theNSString's writeToFile:txtFile atomically:true encoding:(current application's NSUTF8StringEncoding) |error|:(missing value)) as boolean --UTF-8指定、改行コードLF想定
    ------------------------------------------------------------

Add Comment
comment url