AWS LambdaとPythonで実現するPDF編集 ~reportlabとpypdfを活用した実務解説~

AWS LambdaとPythonで実現するPDF編集という解説記事の表紙画像

こんにちは、株式会社シー・エス・エス、デジタル戦略開発課のaaです。
本日は、スキルチェンジプロジェクトを経て、実業務において、AWS Lamda(Python)での開発機会がありましたPDF編集について解説します。

1.仕様要件

前提条件

・編集元のPDFファイル(雛形ファイル)があること。
・S3が利用可能なこと。

 (雛形ファイルのダウンロード、作成ファイルのアップロードに使用)

環境

・Python 3.10.11

・reportlab 4.0.0*1

・pypdf 3.11.0*2

ライブラリインストール手順

pipとsetuptoolsの更新

python -m pip install

ライブラリのインストール

pip install reportlab
pip install pypdf

2.実施手順

2-1.雛形ファイルをダウンロード

import boto3
    s3_client = boto3.client("s3")

    s3_client.download_file(buket, s3downfile_name,temp_pdf)

※事前にS3バケットに編集元のPDFファイル(雛形ファイル)のアップロードが必要  

2-2.編集内容を記載したPDFファイル作成

キャンパスを作成し表示したい内容を座標指定で配置

※詳細は3.PDF編集についてを参照

2-3.雛形ファイルと作成したファイルを重ね合わせPDFファイルを作成

from pypdf import PdfReader, PdfWriter
    # テンプレートPDFファイル(雛形ファイル)
    back_ground_obj = open(temp_pdf, "rb")
    back_ground = PdfReader(back_ground_obj)
    page1 = back_ground.pages[0]
   
    # 編集内容を記載したPDFファイル
    fore_ground_obj = open(make_pdf, "rb")
    fore_ground = PdfReader(fore_ground_obj)
    page1.merge_page(fore_ground.pages[0])

    # 重ね合わせたPDFファイル
    output = PdfWriter()
    output.add_page(page1)
    outputStream = open(overlay_pdf, "wb")
    output.write(outputStream)

    back_ground_obj.close()
    fore_ground_obj.close()
    outputStream.close()

※1~3を作成するPDFファイル分実施

PDF上書きイメージ図

2-4.作成したPDFファイルを1ファイルに結合

from pypdf import PdfFileMerger
    merger = PdfMerger()
    # 統合するPDFファイル
    merger.append(overlay_pdf_A)
    merger.append(overlay_pdf_B)
    merger.append(overlay_pdf_C)
    # 統合したPDFファイル
    merger.write(pdf_mergefilename)
    merger.close()

2-5.完成したPDFファイルをアップロード

import boto3
    s3_client = boto3.client("s3")

    s3_client.upload_file(pdf_mergefilename, buket, s3upfile_name)

 

3.PDF編集について

〇 キャンバス作成(ページ全体設定)

    # 保存先/用紙サイズ
    pdf_canvas = canvas.Canvas("保存先ファイル名", pagesize=(210 * mm, 297 * mm))
    pdf_canvas.setAuthor("作者")
    pdf_canvas.setTitle("表題")
    pdf_canvas.setSubject("件名")

〇 フォント、サイズ指定

    #  フォント、サイズ
    pdf_canvas.setFont("HeiseiKakuGo-W5", 12)

 フォント指定について*3

〇 色指定

    #  色(RGB)
    pdf_canvas.setFillColorRGB(float(0)/255,float(0)/255,float(0)/255,float(1))

〇 テキスト等、オブジェクトの配置は座標指定

    #  テキスト等、オブジェクト
    pdf_canvas.drawString(x * mm, y * mm, "表示内容")

 用紙の座標指定は左下が起点 (A4縦の場合、用紙の左上角の座標は0 , 297 * mm)*4

〇 表(テーブル)

テーブル装飾イメージ図
    # <縦3行・横5行のケース>
    # 表示内容指定
    data = [
        ["項目1","項目2","項目3","項目4","項目5"],
        ["項目6","項目7","項目8","項目9",""],
        ["","項目12","","",""],
        ["","項目17","項目18","項目19",""],
    ]
    # 「項目」にテキストボックスを指定すれば、テーブル項目内の範囲で自動改行
    # テーブル列行のサイズ指定
    table = Table(
        # 表示内容
        data,
        # 各列サイズ
        colWidths=(17 * mm,11 * mm,15 * mm,20 * mm,18 * mm),
        # 各行サイズ
        rowHeights=(38 * mm,40 * mm,50 * mm,42 * mm),
      )
    # テーブルの装飾指定(フォント、枠線、色、結合、表示位置等)
    # 「項目」がテキストボックスの場合はテキストボックス装飾で指定
    table.setStyle(
        TableStyle(
          [
              # フォント・サイズ指定
              ("FONT", (0, 0), (-1, -1), "HeiseiKakuGo-W5", 10),
              # 罫線・色指定
              ("GRID", (0, 0), (0, 0), 1,   colors.black ),
              ("GRID", (2, 0), (2, 0), 1,   colors.black ),
                # 外枠・色指定
              ("BOX", (0, 0), (-1, -1), 1,  colors.black ),
              # 縦線・色指定(左右上下)
              ("LINEBEFORE", (4, 0), (4, 3), 1, colors.black ),
              ("LINEBEFORE", (3, 3), (3, 3), 1, colors.black ),
              ("LINEAFTER", (0, 1), (0, 3), 1,  colors.black ),
              ("LINEABOVE", (1, 3), (3, 3), 1,  colors.black ),
              ("LINEBELOW", (1, 1), (1, 1), 1,  colors.black ),
              # 背景色
              ("BACKGROUND", (2, 0), (2, 0), colors.aqua),
              # 結合
              ("SPAN", (0, 1), (0, 3)),
              ("SPAN", (1, 2), (3, 2)),
              ("SPAN", (4, 0), (4, 3)),
              # 表示位置(上付・中央・下付、左寄・中央・右寄)
              ("VALIGN", (4, 0), (4, 3), "TOP"),
              ("VALIGN", (1, 2), (3, 2), "MIDDLE"),
              ("VALIGN", (0, 1), (0, 3), "BOTTOM"),
              ("ALIGN", (0, 1), (0, 3), "LEFT"),
              ("ALIGN", (1, 2), (3, 2), "CENTER"),
              ("ALIGN", (4, 0), (4, 3), "RIGHT"),
            ]
        )
    )
    # テーブル書出し(範囲、位置指定)
    table.wrapOn(pdf_canvas, (20 * mm, 100 * mm))
    table.drawOn(pdf_canvas, (20 * mm, 100 * mm))

〇 テキストボックス(テーブルの項目に指定した場合、テーブル項目内で自動改行)

    # オブジェクト定義
    style_dict = {
    # フォント
    "fontName": "HeiseiKakuGo-W5",
    # サイズ
    "fontSize": 8,
    # 段落内の行間
    "leading": 8,
    # 書出しのインデント
    "firstLineIndent": 0,
    # 表示位置
    "alignment": TA_LEFT,
    }
    # 装飾定義
    style = ParagraphStyle(**style_dict)
    # テキストボックス
    text_box = Paragraph("テキストボックス表示内容", style)

〇 円、楕円

    # 楕円(座標⇒左下位置、右上位置) 
    pdf_canvas.ellipse(x * mm,y * mm, x * mm,y * mm,)
    # 円(中心座標、半径、枠線描画、塗潰し有無) 
    pdf_canvas.circle(x * mm, y * mm, r * mm, stroke=1, fill=0)

 

4.最後に

GUIで直感的に帳票生成するのではなくコードからPDFを生成するため、表示する内容や編集(装飾)内容についての簡易的な設計書を作成することにより、生成PDFを明確にイメージできるようにしました。また、表示内容を配置する座標位置を把握するのが手間でしたが、PDF標準ツールの「ものさし」使用することにより大まかな位置関係を把握することができました。

5.参考

docs.reportlab.com

6.この記事を書いた人

この記事を書いた人のプロフィール画像

ニックネーム:aa
経歴:入社25年目となります。主に金融・証券・銀行系のシステム開発運用業務に携わってまいりました。現在は金融系のシステム保守開発に従事しております。
一言:健康管理には気を付けたいと思っております。

*1:web画面などをPDF化するのではなく、用紙形式のPDFファイルを編集する対応を前提としています。

  従って、コードからPDFに変換するreportlabを使用します。

*2:ファイルの結合、上書きにはpypdfを使用します。
  本紙利用において支障はないですが、最新バージョンは3.17.1 -2023.11.24現在-

*3:予め組み込まれた日本語フォントは、「HeiseiMin-W3」と「HeiseiKakuGo-W5」。

  標準フォント以外のフォントは、登録することで使用可能。

*4:設定で逆(左上)にすることも可能(Canvas作成時、bottomup=Falseを指定)

  また、単位はpx(ピクセル)だがmm(ミリメートル)を値に乗算することでmm指定可能