質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.78%

VBAでPDFを結合させる場合において、失敗するケース理由がわかりません

受付中

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 1,092

score 204

こちらのサイトを参考にして特定のフォルダでpdfファイルを結合させるコードを作成しております。

PDF環境
Adobe Acrobat 2017
※サイト内でproが必要となっていますが、参照設定でacrobatが選択できており、問題はないと判断しています。

さて、以下のコードの通り、ループをさせて実行させるとCells(6,4)とCells(7,4)のパスにある_combined.pdfだけ4kbのファイルができて、クリックすると「この文書を開くときにエラーが発生しました。このファイルにはページがないため開けません」というメッセージボックスが出現し、結果としてうまくPDFが結合できていないことがわかりました。反対にCells(4,4)とCells(5,4)はうまく結合できております。それぞれpdfファイルが2つずつ入っており、特に権限等にも差がないように思われます。特に失敗しているpdfファイルだけが重いという特徴もございません、

試したこと

①前半がうまくいっていることから、以下のコードのように、waitで処理時間を待機させてみたのですが、特に効果はありませんでした。

②ループではなく直接Cells(7,4)だけを実行してみたのですが、結果は結合に失敗しました。

八方ふさがりな状況で、何か考えられる原因がわかる方、何卒、アドバイスを願えますでしょうか。
宜しくお願い申し上げます。

/*
Cells(4,4) → C:\Users\re\Desktop\2.A会社\#2131_202009AA
Cells(5,4) → C:\Users\re\Desktop\2.B会社\#2141_202009BB
Cells(6,4) → C:\Users\re\Desktop\2.C会社\#2153_202009CC
Cells(7,4) → C:\Users\re\Desktop\2.D会社\#2163_202009DD

*/
Option Explicit
Declare Function StrCmpLogicalW Lib "SHLWAPI.DLL" (ByVal lpStr1 As String, ByVal lpStr2 As String) As Long


Sub combine()
   Dim i As Integer
   Dim cl As String
   Dim fn As String

   Dim startTime As Double
   Dim endTime As Double
   Dim processTime As Double

   startTime = Timer
    i = 4
    Do
       cl = Cells(i, 4).Value
       fn = Mid(cl, InStr(cl, "_") + 1)

       If cl = "" Then
          Exit Do
         End If


        Debug.Print (cl)
        Call Combine_All_PDF(cl, fn)

        Application.Wait Now + TimeValue("00:00:05")
       i = i + 1

     Loop
     endTime = Timer
     processTime = endTime - startTime
     MsgBox "処理が完了しました、経過時間は" & processTime & "です。"
End Sub


Sub Combine_All_PDF(filepath As String, filename As String)

    Dim i As Long
    Dim fs As FileSystemObject
    Dim basefolder As Scripting.Folder
    Dim savepath As String
    Dim st() As String
    Dim mysubfiles As Scripting.Files
    Dim mysubfile As Scripting.File

    Set fs = New Scripting.FileSystemObject



    Set basefolder = fs.GetFolder(filepath)
    Set mysubfiles = basefolder.Files

    i = 0

    For Each mysubfile In mysubfiles

        If fs.GetExtensionName(Path:=mysubfile) = "pdf" Then
            ReDim Preserve st(i)
            st(i) = mysubfile.Path
            Debug.Print st(i)
            i = i + 1
        End If

    Next

'---コード3|抽出したPDFファイルを配列を利用して、名前順に並び替える
    Dim j As Long
    Dim tmp As String

    For i = 0 To UBound(st)
        For j = i To UBound(st)
            Debug.Print st(i), st(j)
            If StrCmpLogicalW(StrConv(st(i), vbUnicode), StrConv(st(j), vbUnicode)) > 0 Then
                tmp = st(i)
                st(i) = st(j)
                st(j) = tmp
                Debug.Print tmp
                Debug.Print st(i)
                Debug.Print st(j)
            End If
        Next
    Next


'---コード4|名前順にPDFファイルを結合していく
    Dim AcroPDDocNew As New Acrobat.AcroPDDoc
    Dim AcroPDDocAdd As New Acrobat.AcroPDDoc
    Dim acroid As Long
    Dim acroGetPages As Long
    Dim acroPages As Long

    acroPages = 0
    acroid = AcroPDDocNew.Create()

    Dim f As Variant

    For Each f In st
        acroid = AcroPDDocAdd.Open(f)
        acroGetPages = AcroPDDocAdd.GetNumPages()
        acroid = AcroPDDocNew.InsertPages(acroPages - 1, AcroPDDocAdd, 0, acroGetPages, True)
        acroid = AcroPDDocAdd.Close()
        acroPages = acroPages + acroGetPages
    Next

    savepath = filepath & "\" & filename & "_combined.pdf"

    '結合したPDFファイルを最後に保存する

    acroid = AcroPDDocNew.Save(1, savepath)
    acroid = AcroPDDocNew.Close()

    'オブジェクトを強制開放する

    Set AcroPDDocAdd = Nothing
    Set AcroPDDocNew = Nothing

End Sub
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • radames1000

    2020/09/23 17:09

    ・Cells(6,4)とCells(7,4)のPDFファイル各二つは手動で問題なく開きますか?
     ⇒そもそもPDFファイルに問題がないか
    ・例えばうまくいったCells(4,4)の中のPDFファイルをCells(6,4)に持ってきたらうまく結合されますか?
    ・逆に、失敗したCells(6,4)の中のPDFファイルをCells(4,4)に持ってきたらうまく結合されますか?
     ⇒フォルダに問題がないか

    キャンセル

  • pegy

    2020/09/23 17:39

    コメントありがとうございます。
    ・手動で問題なく開くファイルです
    ・逆にした場合もそれぞれ開くためフォルダには問題がないようです。

    一点、少し特殊な点がわかりました。それぞれのフォルダに入っているPDFが2つ入っているのですが、そのうちの一つは必ず.msgがファイル(アウトルックのメール.msgをhtml化→PDF化したもの)がオリジナルです。

    こちらは前回の質問の結果で動作させることができました。
    https://teratail.com/questions/292690

    ここで、Cells(6,4)とCells(7,4)の.msgファイルをPDF化したものを見ると、後半の1ページないしは2ページに空白のページがPDF上見て取れます。
    これはCells(4,4)、Cells(4,5)にの中にある同様の.msgをオリジナルとするファイルには存在しない現象です。

    単純に空白のページでも結合されるはずとも思ったのですが、現時点で判明した現象として申し添えます。

    キャンセル

  • pegy

    2020/09/23 17:41

    もちろん、オリジナルのメールファイル.msgには空白のページは存在しません。今並行して調べているのですが、そもそも.msg → html → pdfの過程でなぜ空白のページができてしまうのかが解明できてはいません。

    キャンセル

回答 1

+1

PDFの構造について簡単に説明します。
(今回に関係する所だけ)
「PDFの中にはn番目からlの長さだけXXXの情報が入っています」という情報一覧があります。
その一覧の情報から本当の情報を抜き出してきます。
これの順番と位置との関係が重要で
PDFを生成するソフトによって変わってきたりします。


Aソフト
・文字列だけ左上から順番に設定。
・次に画像だけ左上から順番に設定。

Bソフト
・文字列画像関係なく左上から順番に設定。

Cソフト
・文字列は先に指定した文字列エリアに設定
・画像は先に指定した画像エリアに設定

これはあくまで「PDFを生成するソフト」が「PDFの仕様をどう解釈したか?」の問題で
良し悪しではありません。

Aソフト、Bソフト、Cソフトで作成したPDFを読み取る際は
各ソフトで決めたPDFのルールに基づき読み取るので問題発生は少ないです。
(初期の頃はPDF読み込みエラーなどによくなりました)

ただ今回の様にPDFをAソフトとCソフトで作った形式を結合しようとすると
問題が発生しやすくなります。
大体が「先に指定したPDFの形式にのっとったPDFの結合方法」を行おうとします。
そうすると「後に指定したPDFの形式と齟齬が発生」する場合があります。
それでも「先に指定したPDFの形式」に強引に合わせようとしますので
結果エラーPDFが出来上がるのです。

できればPDFを生成するソフトを統一するのが一番好ましいのですが
できない場合空のPDFを用意して結合のはじめに指定し残り2ファイルを結合指定してみて下さい。
(空.pdf + A.pdf + B.pdf)
場合にもよりますが、エラーにならない場合があります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/09/24 10:42

    コメントを頂き有難うございます。
    同じ形式での出力にそろえることができないかと調べてしました。
    まず、2つのファイルのうち一つ目は複合機から直接PDFで取得(OCRを挟みますが)するもので、もう一つが上記の追加修正にも記載した「https://teratail.com/questions/292690」に基づき、HTMLを経由してVBAのライブラリを用いて出力したものになります。
    前者はハードウェア側の問題もあるので変更は難しいですが、後者について合わせることができないかと検討しております。
    今プロパティ等で仰って頂いたような例えばABCのような形式がどれに該当するのかを調べようと思っていたのですが、調べていても見つけることができず、判別することは可能でしょうか?
    宜しくお願い申し上げます。

    キャンセル

  • 2020/09/24 12:10

    決定的な点が分かりました、msg由来ではなく複合機から取得しているPDFの一部に保護がかかっていることがわかりました。いま、Googleで確認しているのですが、VBAから保護を解除する方法は流石にないですよね?もしご存じであればご教示を頂けると幸いです。
    宜しくお願い申し上げます。

    キャンセル

  • 2020/09/26 01:21

    > VBAから保護を解除する方法
    保護を解除する方法はないですね(保護の意味がなくなるので)
    PDFに対して変更しないようにするのが「保護」ですから今回の結合(A,BファイルからCファイル)
    の場合影響無いかと思うんですが...

    > 例えばABCのような形式がどれに該当するのか
    ソフトは無いですね...
    私の場合PDF初期に頃エラーが多かったせいで構造解析してた頃があって知っていたからで

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る