Power Automate DesktopでPDFのページ数を取得するのは難しい

Power Automate DesktopでPDFのページ数を取得するのは容易ではないという話です。


PDFをページごとに処理するにはページ数が必要

銀行のサイトから計算書を取得しました。1ページ1件で複数ページあります。このファイルから1件ごとにデータを取得したいと思います。

「PDFからテキストを抽出」アクションを使ってループ内で1ページごとデータを取得して処理しようと思いました。

ループを使うにはファイルのページ数が必要です。簡単にPower Automate Desktopで取れるのだろうと思ったらなんとそのような方法が見つかりませんでした。

ありえないページ数を最大値に指定して、見つからなかったらエラー処理という方法も考えられますが好きではありません。


ExtractedPDFTables.Countを使用

丁度今月リリースされたPower Automate Desktop 2.17 には「PDFからテーブルを抽出する」というアクションが追加されています。

このアクションで取得されたデータの Count プロパティを使うとファイルに含まれるテーブル数を取得できます。

今回のPDFファイルは1ページに1つテーブルが含まれていたため、これを使って取得することができました。

例:ExtractedPDFTables.Count

アクションの実行に時間はかかるものの、今回の件では確実にページ数を取得でき、正しく処理できました。


PDFから直接ページ数を取得するのは容易ではない

PDFファイルを直接読み込んでページ数を取得できるのではと思いました。

見て作って学ぶ、PDFファイルの基本構造

しかし、実際にはバージョン違い等があって、容易ではないようです。

EXCEL VBA AcrobatReaderやAdobeを使わずにPDFのページ数を取得する方法

自社で作ったPDFならバージョンやフォーマットがある程度特定できるのでこういった方法も一つだと思います。

しかし、いろんなところからくる請求書を処理するような場合、いつどんなデータがくるのかわかりません。

Power Automate DesktopでPDFのプロパティを取得できるアクションが欲しいと思いました。


試してみると

次のページにVBAでページ数を取得する方法が紹介されていました。

ExcelVBAのみでPDFファイルのページ数を取得する Qiita

PDFファイルの構造上、「/Count 」はソーステキストに1回しか出現しないこと、通常表示される文字列はStreamオブジェクト内に圧縮されているため正規表現にマッチしないことからページ数の抜き出しが可能となっています。

と書かれています。

そこでPower Automate Desktopに移植してみました。

コピーしたコードは次の通りです。Power Automate Desktopの編集画面に貼付すれば復元できます。

Display.SelectFileDialog.SelectFile FileFilter: $'''*.pdf''' IsTopMost: True CheckIfFileExists: False SelectedFile=> SelectedFile ButtonPressed=> ButtonPressed
File.ReadTextFromFile.ReadText File: SelectedFile Encoding: File.TextFileEncoding.UTF8 Content=> FileContents
Text.ParseText.RegexParseForFirstOccurrence Text: FileContents TextToFind: $'''/Count \\d*''' StartingPosition: 0 IgnoreCase: False OccurrencePosition=> Position Match=> Match
Text.GetSubtext.GetSubtextFrom Text: Match CharacterPosition: 7 Subtext=> Subtext

ページ数はSubtext変数に入ります。

実行してみたところExcelから出力したファイルは取得できました。

しかし、家電製品等の取説などは軒並みだめでした。

まずソニーの取説はCountが見つかりませんでした。Encryptという文字があったので暗号化されているようです。もしかしたらそのせいかもしれません。

Chromeで保存したと思われるPDFは次のように複数のカウントがありました。

223 0 obj
<</Type /Pages
/Count 8
/Kids [2 0 R 55 0 R 104 0 R 120 0 R 131 0 R 145 0 R 178 0 R 192 0 R]
/Parent 225 0 R>>
endobj

224 0 obj
<</Type /Pages
/Count 2
/Kids [203 0 R 213 0 R]
/Parent 225 0 R>>
endobj

225 0 obj
<</Type /Pages
/Count 10
/Kids [223 0 R 224 0 R]>>
endobj

これは、ページカウントオブジェクトが階層化されているようです。

この場合 /Parentがないオブジェクトを探し出して処理する必要があります。

少しトライしてみましたが容易ではありませんでした。

/Type /Pagesと/Countの順番が入れ替わっている場合もあるようです。

各キーワードのデリミターもスペースの場合と改行の場合もあります。改行はCRなのかLFなのか、CRLFなのかも考える必要があります。


存在しないページを指定してもエラーが発生しない

好きではないけれど、エラーが発生したらおしまいという方法も試してみようと思いました。

ところが、ループで「PDFからテキストを抽出」アクションを繰り返して、存在しないページに達してもエラーは発生しませんでした。

そしてExtractedPDFTextには最終ページのテキストが入るようです。直前でExtractedPDFTextをクリアしても挙動は一緒でした。

これは困りました。


ファイル分割ではエラーが発生

仕方がないのでファイル分割を試したところ、さすがにこちらはエラーが発生しました。そのため、「ページが範囲外です」エラーが発生したらループを抜けるというフローを書いてみました。

ループ中で、「新しいPDFファイルへのPDFファイルページの抽出」アクションにページ数(LoopIndex)を指定しながら実行します。存在しないページを指定するとエラーが発生するので、LoopIndexから1引いた数がページ数です。

コードは次の通りです。

LOOP LoopIndex FROM 1 TO 1000 STEP 1
    Pdf.ExtractPages PDFFile: $'''C:\\PADTemp\\Sample.pdf''' PageSelection: LoopIndex ExtractedPDFPath: $'''C:\\PADTemp\\OutputFile.pdf''' IfFileExists: Pdf.IfFileExists.Overwrite ExtractedPDFFile=> ExtractedPDF
    ON ERROR PageOutOfBoundsError
        GOTO LastPage
    END
END
LABEL LastPage
Display.ShowMessageDialog.ShowMessage Message: $'''ページ数は %LoopIndex - 1%''' Icon: Display.Icon.None Buttons: Display.Buttons.OK DefaultButton: Display.DefaultButton.Button1 IsTopMost: False ButtonPressed=> ButtonPressed

デスクトップフローとして部品化してもよいかもしれません。

実際に使うとなると、分割ファイルのパスについて考慮が必要です。固定ファイルにするのか、一時ファイルとして処理するのか。分割ファイルの最後は削除するのかどうかも考慮する必要があります。他のエラーが発生したときのTry Catchも必要でしょう。

ページ数が最大値に達したときの処理も必要かもしれません。ありえないページ数にしておけば現実的には問題ありませんが。


コメント

アクセス数の多い投稿

セキュリティ対策ソフトのノートンが詐欺ソフトまがいになってしまってショック

Teamsで日本語入力すると左上に変換ウィンドウが出る

突然滅茶苦茶遅くなったPCがWindows Updateのキャッシュクリアで復活

Amazon Prime Videoで4K UHD映画を検索する方法

Excel 2019 クエリが原因で日本語入力の一文字目が勝手に確定する

iPhone 12 Proと iPhone 8 Plusのサイズを比較

Microsoft Flight Simulator (2020)のPS4コントローラー設定

次期主力Wi-Fiルーター NEC Aterm WX5400HP

DELL 31.5インチ 4K HDR モニター U3219Q を購入

Excel VBAからODBCを使ってデータを簡単に取得する