HOKKAPOKKA
@HOKKAPOKKA (HOKKA POKKA)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

大量のPDFをWindows10でコマンドで結合させたい

解決したいこと

大量のPDFをWindows10でコマンドで結合させたいが、エラーになって落ちる

例)
Windows10 16GBメモリーのパソコンで、コマンドベースで200k程度のpdfファイル(各1ページ)7000個を結合したく、
pdftkというツールで次のように打つと、しばらくしてからエラーになり、pop-upにはtoo many heap sections と
出ます。
このエラーの一般的対処として、下記のガイドレジストリの変更をしてみましたが、効果がありません。
https://learn.microsoft.com/ja-jp/troubleshoot/windows-server/performance/desktop-heap-limitation-out-of-memory

おそらくこのツールの使い方を変えて対処するしかないと思うのですが、調べてみると下記のLinkがありました。
https://unix.stackexchange.com/questions/605705/pdftk-too-many-heap-sections-increase-maxhincr-or-max-heap-sects
バッチファイルに疎いのでよくわからないのですが、これはどういう意味なのでしょうか。

それとも、他の方法か他のツールをどなたか思いつきますでしょうか。(Adobe Acrobat Proは所有していません)
ちなみに私が試したコマンドは
pdftk c:\temp2*.pdf cat output c:\temp2\combined7000.pdf
すべてのpdfファイルはc:\temp2\にあります。

発生している問題・エラー

too many heap sections

自分で試したこと

レジストリの変更

0

4Answer

参照されていた、以下のコマンドのことでしょうか?

for i in $(seq 1 10); do
   if [[ $i -eq 1 ]] ; then
        cp $i.pdf a.tmp.pdf
    else
        pdftk a.tmp.pdf $i.pdf output b.tmp.pdf
        mv b.tmp.pdf a.tmp.pdf
    fi
done
mv a.tmp.pdf total.pdf

上のコマンドの意味は、

1.pdf 2.pds 3.pdf ・・・ 10.pdf があったときに、
これらを一つづつ結合して最終的に、1〜10.pdf を結合して total.pdf にするというものです。
ループで処理しているのを、コマンドベースにばらすと以下のようになります。

# 1つ目のファイル(1.pdf)
1.pdf を a.tmp.pdf にコピー
# 2つ目のファイル(2.pdf)
a.tmp.pdf に 2.pdf を結合した結果を、b.tmp.pdf にする
b.tmp.pdf を a.tmp.pdf にリネーム
# 3つ目のファイル(3.pdf)
a.tmp.pdf に 3.pdf を結合した結果を、b.tmp.pdf にする
b.tmp.pdf を a.tmp.pdf にリネーム
# 4つ目以降のファイル(4.pdf)
# 同様に10.pdfまでこの2行を繰り返す。
a.tmp.pdf に (n).pdf を結合した結果を、b.tmp.pdf にする
b.tmp.pdf を a.tmp.pdf にリネーム
# 1.pdf 〜 10.pdf の結合が終わったら
a.tmp.pdf を total.pdf にリネーム

ただし、I have not tried this.とあるように、これ自体の動作検証はやっていないようです。

私もpdftkに触れたことがなく、動作させる環境もないので、確かめることはできません。
そもそも上のコマンドは、unix/linuxのビーシェルと思われるので、Windows10で動作させるのは工夫が必要ですが、Windowsのバッチファイルでも同様なことは実装できるとお思います。

ループ処理としなくても、Excelなどでコマンド文字列を大量に生成して、バッチファイルにする方法もあります。

例えば、以下のような感じです。

pdf_concat.bat
@echo on
' f0001.pdf 〜 f7000.pdf を結合して combined7000.pdf を作成する
cd c:\temp2
copy /b f0001.pdf a.tmp.pdf '1個目のファイル
pdftk a.tmp.pdf f0002.pdf cat output b.tmp.pdf & rename b.tmp.pdf a.tmp.pdf '2個目のファイル
pdftk a.tmp.pdf f0003.pdf cat output b.tmp.pdf & rename b.tmp.pdf a.tmp.pdf '3個目のファイル
pdftk a.tmp.pdf f0004.pdf cat output b.tmp.pdf & rename b.tmp.pdf a.tmp.pdf '4個目のファイル
pdftk a.tmp.pdf f0005.pdf cat output b.tmp.pdf & rename b.tmp.pdf a.tmp.pdf '5個目のファイル
    :     :     :     :
pdftk a.tmp.pdf f7000.pdf cat output b.tmp.pdf & rename b.tmp.pdf a.tmp.pdf '7000個目のファイル
rename a.tmp.pdf combined7000.pdf

すみませんが、私も動作させたわけではありませんので、悪しからず。

0Like

Comments

  1. @HOKKAPOKKA

    Questioner

    nak435さん、感謝します。
    この約7000行のバッチをexcelで作れるというのは本当でしょうか。方法を伝授いただけますと、すぐ実践してみます。
    あるいはdosのバッチのループでできないかと。

  2. 方法を伝授いただけますと、すぐ実践してみます。

    特に確立された方法がある訳ではありませんが、(上記例のf0001.pdf〜f7000.pdfのように)ファイル名が数値の連番であれば、Excelのフィル機能で7000行の文字列を簡単に作れます、ということです。それをコピペしてバッチファイルとする。

    あるいはdosのバッチのループでできないかと

    できると思います。
    ↓こちらにWindowsバッチのループ処理の書き方が詳しく説明されていましたので、参考にされてはどうですか(不明点が出たら、新たにQ&Aをアップする)。

```bat
  コマンドは半角```で囲みましょう!
```

コマンドと引数の意味を理解しましょう。

rename 1個目.pdf 前.pdf
:================
rename 2個目.pdf 後.pdf
pdftk  前.pdf 後.pdf cat output 出力.pdf 
rename 前.pdf   1個目.pdf
rename 後.pdf   2個目.pdf
rename 出力.pdf 前.pdf
: ================
rename 3個目.pdf 後.pdf
pdftk  前.pdf 後.pdf cat output 出力.pdf 
rename 前.pdf   2個目.pdf
rename 後.pdf   3個目.pdf
rename 出力.pdf 前.pdf

入力pdf は3個以上指定できるのでしょうか? 最大何個まででしょうか?

フォルダー内のファイル名を1個1個、取得しましょう。

バッチファイルはダーク感満載です。

▼ポエム

そもそも、pdftkが c:\temp2*.pdf の指定で 何ファイル目で、ヒープ領域不足になっているのでしょうか?

SharedSection=1024, 12288,512

pdftk c:\temp2*.pdf cat output c:\temp2\combined7000.pdf

切り分け

echo c:\temp2*.pdf 
dir  c:\temp2*.pdf 
0Like

Comments

  1. @HOKKAPOKKA

    Questioner

    ご指導いただきありがとうございます。
    なんとか1000個のテスト用PDFファイル F0001.pdf ~ F1000.pdfをExcelの力を借りて作りまして、
    ついでながらfiles_list.txt というテキストファイルを作り、ファイルリストの代わりにしました。
    files_list.txtの中身は、シンプルに以下です

    F0002
    F0003
    F0004
    .
    .
    .
    F1000

    これを作ったあとで、上記1001個のファイルを全てC:\tempに入れて、以下のバッチファイルも
    そのフォルダに作りました。(pdf1000_connect.bat)

    @echo on
    copy /b /y f0001.pdf a.tmp.pdf
    for /f %%a in (files_list.txt) do (
    echo %%a
    pdftk a.tmp.pdf %%a.pdf cat output b.tmp.pdf
    copy /b /y b.tmp.pdf a.tmp.pdf
    echo ----------------------------------
    )
    copy /b /y a.tmp.pdf combined1000.pdf

    なんだかやぼったいですが、なんとかできました! テスト用pdfファイルはすべて48KBのちいさなファイルですが、出来上がりのcombined1000.pdfは41MBで、バッチが終わるまで2時間かかりました。
    試したパソコンは24GBのメモリーを積んでいますが、レジストリはいじっていません。

    ところで、今回はfiles_list.txtを1行づつ読むループで作りましたが、実際の業務では、対象の
    pdfファイルの数も名前も可変なのです。
    すいません、もう少しお知恵をお貸しください。
    上記の例で、ファイル名や数が変わろうとも、ファイルリストを作らずともc:\tempにあるpdfをかたっぱしから存在するものすべてを今回のようにpdftkの追記式で1つのファイルにするようにバッチファイルは改造できるのでしょうか。特にループにはこだわっていませんが、ワイルドカード指定で
    c:\temp*.pdfを指定することなどできるのかわからないので。

実際の業務では、対象のpdfファイルの数も名前も可変なのです。

名前が可変となると、結合する順番が決められませんので、名前に(順番が一意に決まる)規則性が必要です。
それとも、結合する順番は順不同でよいのでしょうか?

0Like

Comments

  1. @HOKKAPOKKA

    Questioner

    はい。ファイル名がdir出力で出てくる順番に結合されることは了解を得ています。
    さすがにワイルドカード指定時の取得される順番までは制御できないでしょうから。
    あとは、運用上、ファイルをrenameして調節します。

  2. 以下でどうでしょうか?

    cat_pdf.bat
    @echo off
    setlocal enabledelayedexpansion
    
    rem 結合するPDFファイルのフォルダへ移動
    cd /d f:\temp
    
    set /a cnt=0
    for %%f in (*.pdf) do (
    	set /a cnt=!cnt!+1
    	echo !cnt! 番目のファイルを処理中
    	if !cnt! equ 1 (
    		copy /b /Y %%f a_tmp.pdf
    	) else (
    		pdftk a_tmp.pdf %%f cat output b_tmp.pdf
    		copy /b /Y b_tmp.pdf a_tmp.pdf
    	)
    )
    
    rem 結合したファイルを格納
    copy /b a_tmp.pdf ..\total.pdf
    
    del a_tmp.pdf b_tmp.pdf
    
    echo 合計 %cnt% ファイルを結合しました
    

    バッチが終わるまで2時間かかりました。
    試したパソコンは24GBのメモリーを積んでいます

    メモリ容量は十分なので、ディクスの入出力性能に大きく依存すると思います。

  3. 少しでも処理を早くするなら、

    copy /b /Y b_tmp.pdf a_tmp.pdf
    

    del a_tmp.pdf
    rename b_tmp.pdf a_tmp.pdf
    

    に変更すると、ファイルコピーを無くして高速化できます。
    しかし、数が多いだけに一晩では終わらないかも知れません。

  4. 数が多いだけに一晩では終わらないかも知れません。

    試算したところ、一晩どころでは無いです。
    上にご提示した単純ループ処理の方式では、7000ファイルの結合には数ヶ月かかるのではと思います。

    質問の最初に戻りますが、*.pdfですべてのファイルを一度にはエラーとなり無理とのことでしたが、では、何ファイルまでなら1回のコマンドで結合できますか?
    この数の最大を調べてもらって、その数ごとに分割結合することが最善かと思います。
    (この数の最大を二分探索法で調べれば10数回で調べられます)

    二分探索法の補足;
    7000は無理だと分かっているので、その半分の3500を調べます。
    3500がOKなら、(7000+3500)÷2の5250を調べます。=> OKなら6125、NGなら2625を調べる。
    3500もNGなら、(3500+0)÷2の1750を調べます。=> OKなら2625、NGなら875を調べる。
    ・・・のように、OKならその上の半分のところ、NGならその下の半分のところを次々に調べていく方法。
    半分、そのまた半分と繰り返すと、最後はOKとNGの境界のOKの最大ファイル数が求まります。

  5. @HOKKAPOKKA

    Questioner

    @nak435さん、ご親切にありがとうございます。
    いただいたバッチファイルのcopyの行をdel とrenameに書き換えてやってみたら、
    1000ファイルで1時間54分 と、若干ですが早くなりました。
    おっしゃるような分割も一時考えたのですが、いずれにしてもどこかで人が介入することになるのでは?と思い、やめました。

さらに確認ですが、
7000本のPDFの結合は、今回限りでしょうか。
月に一回とか、今後も定期的に行うのでしょうか?

0Like

Comments

  1. @HOKKAPOKKA

    Questioner

    1000本の結合は月1回、7000本は年1回です。

  2. 差し出がましいかと思いますが、

    100ファイルづつのPDFに結合し、最後に全体を結合するように工夫してみました。

    @echo off
    setlocal enabledelayedexpansion
    @echo 開始 %date% %time%
    
    rem 結合するPDFファイルのフォルダへ移動
    cd /d f:\temp
    
    rem work ディレクトリ作成
    mkdir work
    
    rem 分割結合する最大
    set /a split_cnt=100
    
    @echo 結合するPDFの総数をカウント中...
    set /a file_cnt=0
    for %%f in (*.pdf) do (
    	set /a file_cnt=!file_cnt!+1
    	set filename[!file_cnt!]=%%f
    )
    
    @echo 結合するPDFの総数:%file_cnt%
    @echo %split_cnt% づつのPDFに分割して結合し、最後に全体を結合します
    
    for /l %%n in (1,%split_cnt%,%file_cnt%) do (
    	set /a max=%%n+%split_cnt%-1
    	if !max! gtr !file_cnt! ( set /a max=!file_cnt! )
    
    	set filenames=
    	for /l %%m in (%%n,1,!max!) do (
    		set filenames=!filenames! !filename[%%m]!
    	)
    
    	@echo %%n!max! のファイルを結合 work\F%%n-!max!.pdf at !date! !time!
    	pdftk !filenames! cat output work\F%%n-!max!.pdf
    )
    
    @echo 全体を結合 at !date! !time!
    pdftk work\F*.pdf cat output ..\total.pdf
    
    rem 後始末(下記コマンドを手動で実行すること)
    rem rmdir /s /q work
    
    @echo 終了 %date% %time%
    

    1000ファイルなら1分程度、7000ファイルでも10分程度ではないかと思います。
    (すみません、ディスクの性能に依存します)
    ご参考にしてください。

  3. @HOKKAPOKKA

    Questioner

    @nak435さん、差し出がましいなんてとんでもございません! ありがとうございます。
    やってみました。なんと1分20秒で終わりました。感動です!!!
    今後はPDF1個あたりのサイズが肥大化することもあるので、このバッチファイルを大事に育てて行こうと思います。qiitaに相談してよかったです。本当にありがとうございました。

  4. @HOKKAPOKKA

    Questioner

    100ファイルづつのPDFに結合し、最後に全体を結合するように工夫してみました。
    テスト用のファイル(F0001.pdf~F1000.pdf)では期待通りに動いた記憶があるのですが、実際に実環境で雑多なPDFの入った環境でやると、このエラーで止まります。残念でなりません。
    バッチファイルの5行目は実際にPDFファイルの実在するフォルダに変えましたが、ほかにファイル名の規則性がそろっている必要はあるのでしょうか。
    ちなみにまとめる対象pdfファイルはほとんどファイル名に漢字が含まれています。これが原因だったりしますか?
    エラー.png

  5. サブディレクトリまたはファイル work は既に存在します

    実際にPDFファイルの実在するフォルダでバッチファイルの下から2行目の、コメントアウトしているrmdir /s /q workを実行してください。
    今後もバッチを開始する前に手動で実行が必要です(6行目に移動する方法もある)。

    ファイル名の日本語も怪しいですが、まず最初のエラーを解決してもう一度実行してみてください。

  6. 実際に実環境で雑多なPDFの入った環境でやると、・・・

    この雑多な中で、結合するPDFファイルに、ファイル名の規則性があるのでしょうか?
    バッチファイルの*.pdf部分を、結合対象のPDFファイルだけが抜き出せるように変更してください。(スペースで区切れば複数可。ex., (A*.pdf B*.pdf C*.pdf)

    for %%f in (*.pdf) do (
    

    それができないのなら、別なフォルダに結合するPDFファイルを抜き出して、対象とするフォルダをそのフォルダに変更してください。

  7. @HOKKAPOKKA

    Questioner

    すいません、このバッチ、バッチ自体が存在するフォルダの中にある*.pdfは、
    名前を問わずすべて結合対象になっているのかと早とちりをしておりました。

    ファイルの規則性ですが、このような規則性はあります。
    〇〇調査結果_部署名_氏名カナ.pdf
    サフィックスが増えるような類ではありません。

    例として
    〇〇調査結果_営業部_イソノカツオ.pdf
    〇〇調査結果_営業部_フグタタラオ.pdf
    〇〇調査結果_営業部_クマダマサシ.pdf
    〇〇調査結果_企画部_イソノナミヘイ.pdf
    〇〇調査結果_企画部_ヤマダタロウ.pdf

    このように、○○調査結果*.pdfがワイルドカード表記になります。
    ファイル数は稼働時期によりまちまちです。
    また、たまに以下のようなpdfが同一フォルダに混ざることがあります。
    これも結合対象になります。
    調査結果サマリー202306.pdf
    これは、1つのpdfに結合したときに何ページ目に来ても問題ありません。

  8. このバッチ、バッチ自体が存在するフォルダの中にある*.pdfは、
    名前を問わずすべて結合対象になっているのかと早とちりをしておりました。

    いいえ、その通りです。*.pdfとは、そうゆことです。

    その前に回答したrmdir /s /q workを実行してもう一度実行してみましたか?

  9. @HOKKAPOKKA

    Questioner

    ちなみに rmdir /s /q work を実行してからバッチ実行をしても現象は変わりませんでした。

  10. サブディレクトリまたはファイル work は既に存在します

    このエラーも、出たままですか?
    実行したフォルダが違っていると思います。
    バッチの中身全体と、実行したDOS窓のスクショを貼ってください。

  11. @HOKKAPOKKA

    Questioner

    以下のpdf_connect.batを流しました.プロンプトは添付の画像です。

    @echo off
    setlocal enabledelayedexpansion
    @echo 開始 %date% %time%

    rem 結合するPDFファイルのフォルダへ移動
    cd /d C:\Kenshin

    rem work ディレクトリ作成
    mkdir work

    rem 分割結合する最大
    set /a split_cnt=100

    @echo 結合するPDFの総数をカウント中...
    set /a file_cnt=0
    for %%f in (*.pdf) do (
    set /a file_cnt=!file_cnt!+1
    set filename[!file_cnt!]=%%f
    )

    @echo 結合するPDFの総数:%file_cnt%
    @echo %split_cnt% づつのPDFに分割して結合し、最後に全体を結合します

    for /l %%n in (1,%split_cnt%,%file_cnt%) do (
    set /a max=%%n+%split_cnt%-1
    if !max! gtr !file_cnt! ( set /a max=!file_cnt! )

    set filenames=
    for /l %%m in (%%n,1,!max!) do (
    	set filenames=!filenames! !filename[%%m]!
    )
    
    @echo %%n~!max! のファイルを結合 work\%%n-!max!.pdf at !date! !time!
    pdftk !filenames! cat output work\%%n-!max!.pdf
    

    )

    @echo 全体を結合 at !date! !time!
    pdftk work*.pdf cat output ..\total.pdf

    rem 後始末(下記コマンドを手動で実行すること)
    rem rmdir /s /q work

    @echo 終了 %date% %time%
    無題.png

  12. workのエラーは消えてました。

    日本語ファイル名の切り分けしますから、以下のコマンドの結果を貼ってください。

    dir C:\Kenshin
    
    cd /d C:\Kenshin
    for %%f in (*.pdf) do (
        @echo %%f
    )
    

    現象が発生する、最低限のファイルに絞って実施し、個人名とかはマスクしてください。

    エラーメッセージにY.pdfがありますが、存在しませんか?

  13. @HOKKAPOKKA

    Questioner

    すいません、テスト環境を整理してしまったので、Y.pdfの存在は今となってはわからないんです。
    まず、テストデータとして以下のような105のファイル名にして、その5個を置いてもともとのバッチを実行すると、以下の画面になります。(止まってしまったのでCtrl+cでブレークしました)
    健保提供データの本部提出用加工__イソノナミヘイ.pdf

    image.png

    また、これは漢字が原因かと思い、105個のファイルをすべてシングルバイト名(aaa.pdfとか)に変更して25個で実施したのが以下です。
    image.png

    このシングルバイト名25個のdir出力
    image.png

    その時にこれをやったときの次のバッチ出力 cd /d C:\Kenshin
    for %%f in (*.pdf) do (
    @echo %%f
    )
    image.png
    ※途中で切れています

    漢字名105ファイルの際のdir出力
    image.png

    漢字名105ファイルの際の次のバッチ出力 cd /d C:\Kenshin
    for %%f in (*.pdf) do (
    @echo %%f
    )
    image.png
    ※途中で切れています

  14. 1)バッチファイルに\の抜けがあります。まず、それを修正してください
    (シングルバイト名(aaa.pdfとか)に変更して実施したエラーの原因)

    - pdftk work*.pdf cat output ..\total.pdf
    + pdftk work\*.pdf cat output ..\total.pdf
    

    2)スペースを含んだ日本語ファイル名が使われているので、以下の箇所に""を入れてください

    - set filename[!file_cnt!]=%%f
    + set filename[!file_cnt!]="%%f"
    

    3)日本語の文字コードにSJISとUTF-8があり、バッチファイルとDOS窓で統一されていない可能性があります(違うかも)

    問題切り分けのため、まず、バッチを実行するDOS窓を開き、コマンドchcpを打つ。おそらく、現在のコード ページ:932と出力されるはずです(SJISの意味)。
    この状態でdirで日本語ファイル名が文字化けせずに表示されているなら、ファイル名の文字コードもSJISということが分かります。

    次に、当該バッチファイルをWindows標準「メモ帳」で開き、ステータスバー左下を見てください。ここにUTF-8と出ていたら、バッチファイルの文字コードはUTF-8で、混在が原因です。

    encode.png

    その場合は、メモ帳で「名前を付けて保存」を選び、文字コードにANSIを選び、バッチファイルをSJISで保存します。これでバッチファイルとDOS窓とファイル名で文字コードが一致したはずです。

    save.png

    以上の仮定と違いがあれば、バッチファイルの先頭echo offecho onに変えて再度実行しスクショを貼ってください。

  15. @HOKKAPOKKA

    Questioner

    ご指摘いただいた3点の修正に加えて、バッチの以下の部分を修正することで、動きが安定しました。今のところ文字化けもありません。しばらくこれで使ってみます。ありがとうございます!!
    pdftk work*.pdf cat output ..\total.pdf → pdftk work*.pdf cat output total.pdf 

  16. 動いてよかったですね。
    今後、Q&Aが発生した時は、新たな質問として投稿してください。その際は、最初からnak435にメンションしてもらって結構です。

Your answer might help someone💌