pythonのPDFライブラリを使ってエクセルでPDFの結合とリサイズを行う

PC関連

既にあるPDFとエクセルファイルをまとめてPDF化してA4サイズにリサイズしてほしいとの要望がありました。
エクセルのVBAを使えばPDFファイルとして保存することができますが、加工することがでません。
そこでPDFの加工はpythonライブラリの「pypdf」を使ってPDFの追加とリサイズを行う対応をしました。
今回、「pypdf」を使ってPDFの追加とリサイズを行うツールとプログラムを公開しますのでご活用ください。

PDF結合&リサイズ ツール(エクセル)

ツールの説明

ファイル名の先頭部分が同じPDFを結合します。
結合したファイルはA4サイズにリサイズして出力します。
また、誰でも扱えるように入出力の画面としてエクセルを使用しています。

※ 利用者はpythonなどの開発環境がなくても実行できます。
※ python部分のプログラムだけでもフォルダ内のPDFの結合とリサイズはできます。
※ エクセルはPDF出力は可能ですが結合リサイズなどの加工は
  無料では難しい為、処理はPyton上でおこなっています。

ダウンロード

PDFの結合・リサイズアプリ

使い方

1.結合元フォルダ(01_src)と追加フォルダ(02_add)に結合したいPDFをいれてます。
2.pdf_join.xlsmを開き「PDF JOIN」ボタンを押します。
3.結合フォルダ(03_out)に結合したPDF、リサイズフォルダ(04_out_resize)にA4にリサイズしたPDFを出力します。
結合結果は別エクセル(pdf_join_YYYYMMDD.xlsm)で確認できます。

開発の流れ

開発環境を整える

 ・開発環境を作成する (VSCodeなどお好きなIDE(統合開発環境))
 ・python(自分は3.10.9)をインストールする
 ・エクセルからPythonが利用できるようにopenpyxl、pyinstallerをインストールする
  pip install openpyxl pyinstaller
  ※pyinstallerを呼び出すためにpathを通します。
    set path=C:\Users[ログインアカウント名]\AppData\Roaming\Python[Pythonバージョン]\Scripts\:%path%
  ・PDFライブラリー(pypdf)のインストールする
  pip install pypdf

入出力用のエクセルを作成する

  上記のダウンロードのエクセルファイルを使用してください。

PDF結合、リサイズ用のpythonプログラムを作成する

以下のPythonプログラムと同じばしょに結合元のフォルダ「01_src/」、追加のフォルダ 「02_add」、出力のフォルダ「03_out」、リサイズ後の出力のフォルダ「04_out_resize」を作成してください。

結合元のフォルダと追加のフォルダ にPDFファイルを格納して実行すると、出力フォルダに結合さいたPDFを出力します。

【pdf_join.pdf】

import glob
import sys,os
from pypdf import PdfMerger
from pypdf import PdfWriter, PdfReader
import openpyxl 
import datetime

#動作確認したモジュールのバージョン:pypdf 3.9.1
# モジュールのバージョン
# print(f"{pypdf.__name__} バージョン: {pypdf.__version__}")

# A4サイズ’210mm x 297mm )のピクセル値
A4_pix_width= 595
A4_pix_height = 842

# 各必要なフォルダパス
program_directory = os.path.dirname(os.path.abspath(sys.executable))

# 結合元のフォルダ ./01_src
srcFolder = program_directory+"/01_src/"

# 追加のフォルダ ./02_add
addFolder = program_directory+"/02_add/"

# 出力のフォルダ ./03_out
outFolder = program_directory+"/03_out/"

# リサイズ後の出力のフォルダ ./04_out_resize
outFolder_resize = program_directory+"/04_out_resize/"

# 結合元のファイルをリストで取得
srcPdfs = glob.glob(srcFolder + "*.pdf")

# 追加のファイルをリストで取得
addPdfs = glob.glob(addFolder + "*.pdf")

# 結合元のファイル名
srcFileName = ""
# 結合元のコードID
srcFileID = ""
# 追加ファイル名
addFileName = ""

# 追加フラグ
isAdd = False

# 表示用追加ファイル(カンマ区切り)
disAddFile = ""

# 結果を記載するエクセル
wb = openpyxl.load_workbook( filename = program_directory +"/PDF結合.xlsm", keep_vba=True, data_only=True)
ws = wb[ "PDF結合" ]

# 記載範囲を初期化する
for row in ws.iter_rows(min_row=7, min_col=2, max_row=3000, max_col=5):
  for cell in row:
    cell.value = None
rowNo = 7

#結合先フォルダを初期化する 
for f in os.listdir(outFolder):
    os.remove(os.path.join(outFolder, f))

#リサイズ先フォルダを初期化する 
for f in os.listdir(outFolder_resize):
    os.remove(os.path.join(outFolder_resize, f))

# *******   PDF追加処理  *********
for srcFile in srcPdfs:
    # PDFマネージャーの初期化
    merger = PdfMerger()

    # 元ファイルを設定する
    merger.append(srcFile)

    # ファイル名を取得する
    srcFileName = os.path.split(srcFile)[1]
    srcFileID = srcFileName.split('_')[0]

    # 追加フラグ、追加ファイルを初期化する
    isAdd = False
    disAddFile = ""

    for addFile in addPdfs:
        addFileName =os.path.split(addFile)[1]
        if(srcFileID == addFileName.split('_')[0]):
            merger.append(addFile)
            isAdd = True
            if not disAddFile :
                disAddFile = addFileName
            else :
                disAddFile = disAddFile + "," +addFileName

    # 元ファイル名でoutフォルダに出力する
    merger.write(outFolder + srcFileName )

    # 結合元をエクセルに出力
    ws.cell(rowNo, 2).value = srcFileName
    ws.cell(rowNo, 5).value = srcFileName

    # 追加ファイルをエクセルに出力
    print(disAddFile)
    ws.cell(rowNo, 3).value = disAddFile
    
    # 結合方法をエクセルに出力(追加フォルダに対象のファイルがない場合はCOPY表記)
    ws.cell(rowNo, 4).value = 'ADD' if isAdd else 'COPY'
    rowNo = rowNo + 1

merger.close()

# 現在日時を取得
dt_now = datetime.datetime.now()
seveFileName = program_directory + "/pdf結合"+"【結果】" + dt_now.strftime('%Y%m%d_%H%M%S') +".xlsm"

# エクセルを別名で保存する
wb.save(seveFileName)

# *******   リサイズ処理  *********
# outフォルダのファイルをリサイズする  
for f in os.listdir(outFolder):
    # リサイズ元のフォルダ+ファイル名
    srcin_file = os.path.splitext(outFolder)[0] + f 

    # リサイズ後のフォルダ+ファイル名
    output_file = os.path.splitext(outFolder_resize)[0] + f 
    
    # PDFリーダーの初期化
    pdf_reader = PdfReader(srcin_file)

    # PDFライターの初期化
    pdf_writer = PdfWriter()

    # 全ページを処理する
    for i in range(len(pdf_reader.pages)):
        page = pdf_reader.pages[i]
        # ページの縦横サイズを取得
        width = page.mediabox.width
        height = page.mediabox.height

        # 横長ページの対応
        if width > height:
            page.rotateCounterClockwise(90)
            width, height = height, width

        # A4サイズに変換の倍率
        scale = min(float(A4_pix_width / width), float(A4_pix_height / height))

        # A4サイズに変換
        page.scale_by(scale)
        
        # 変換したページを新しいPDFファイルに追加
        pdf_writer.add_page(page)
        
    # 新しいPDFファイルで保存
    with open(output_file, 'wb') as f:
        pdf_writer.write(f)

Pythonをエクセルで利用できるようにPythonプログラムexeファイルにする

pyinstaller プログラム名 –onefile –noconsole
 ビルドが完成したら「dest」フォルダが作成され中にexeファイルができる
 exeファイルはエクセルとフォルダ内(同じ階層)に配置する

作成した入出力用のエクセルに「exeファイル」を呼び出すボタンを作成し以下のマクロを設定する。

  ※ダウンロードのエクセルファイルでの確認できます。
  Sub pdf_join_button()
   Shell ThisWorkbook.Path & “/pdf_join.exe”
   MsgBox (“PDF結合完了しました”)
  End Sub

結合元フォルダ、追加フォルダにPDFを格納する

結合元フォルダ(01_src)と追加フォルダ(02_add)に結合したいPDFをいれてます。

エクセルボタンを押して実行する

結合フォルダ(03_out)に結合したPDF、リサイズフォルダ(04_out_resize)にA4にリサイズしたPDFを出力します。
結合結果は別エクセル(pdf_join_YYYYMMDD.xlsm)で確認できます。
ファイル名の先頭部分が同じPDFが無い場合はそのまま出力(COPY)、ある場合は結合(ADD)と記載します。

あとがき

エクセルからpythonのライブラリを使用することでできることで新しい機能を増やすことができます。ただし、実行ファイルが20数MBと大きくなるため配布はむつかしそうです。

コメント