おはようございます。まさきんです。
健康保険の給付申請書を作ろうとしたら、PDFに文字を入力できませんでした。記入欄がない、いわゆる平らなPDFです。AIに座標で文字を重ね打ちさせて印刷用に仕上げたので、その流れをメモしておきます。
※記事公開時点の情報です。最新の条件は公式サイトでご確認ください。
- 入力できないPDFの申請書は、記入欄(AcroForm)を持たない「平らなPDF」のことが多い
- 道具は3つ。pdfplumber で罫線の座標を拾い、reportlab で文字を描く。重ねるのは pypdf
- 座標の原点は pdfplumber が左上、reportlab が左下。上下が逆なので変換が要る
- A4は 約595×842pt 。1ptは1/72インチで、座標はすべてこの単位で考える
- 口座番号・自署・押印の3か所は重ね打ちせず、印刷してから手書きにした
記入欄のないPDFに当たった
作ったのは健康保険の給付申請書です。立て替えた医療費の払い戻しを受けるための書類ですね。制度の流れは立て替えた医療費を療養費と医療費助成で取り戻す手順に書いています。
申請書はPDFで配られていました。ただ、開いても欄をクリックできません。文字を打つ場所がどこにもない状態です。用紙は2枚ありました。
印刷して全部手書きする手もあります。とはいえ、同じ数字や期間を何か所も書くのは少し面倒です。書き損じたら刷り直しになります。なので、打てる部分だけAIに打たせることにしました。
平らなPDFの見分け方
PDFの申請書には2種類あります。入力欄が埋め込まれたものと、ただの絵として罫線が描かれたものです。
Adobeの公式ヘルプでは、後者を「flat form」と呼んでいます。作成者が入力欄を付けていないフォーム、という説明でした。この記事では平らなPDFと呼ぶことにします。
見分け方は簡単です。
- PDFを開いて欄をクリックする。カーソルが出なければ平らなPDFの可能性が高い
- Tabキーを押しても、欄から欄へ移動しない
- AIに「このPDFに入力欄があるか調べて」と頼む
3つめは、pypdf というライブラリで確かめられます。公式ドキュメントにある get_fields() を呼ぶだけです。入力欄がなければ None が返ります。
from pypdf import PdfReader
reader = PdfReader("form.pdf")
print(reader.get_fields()) # 入力欄がなければ None
僕の申請書は、ここで入力欄なしと分かりました。入力欄があるPDFなら、この記事の方法は要りません。ふつうに欄へ入力する方が確実です。
使う道具は3つ
AIに使わせた道具は、どれも無料のPythonライブラリです。役割がきれいに分かれています。
| 道具 | 役割 | この作業で使う機能 |
|---|---|---|
| pdfplumber | 元のPDFを読む | 罫線や四角の座標を取る。ページを画像にする |
| reportlab | 新しいPDFを描く | 透明な紙に文字だけを置く |
| pypdf | PDF同士を重ねる | 入力欄の有無を調べる。文字の紙を用紙に重ねる |
イメージは透明なシートです。申請書の上に透明シートを置きます。そこに文字だけを印字して、2枚を貼り合わせる。やっていることはそれだけです。
元のPDFそのものは書き換えていません。なので、ズレたら透明シートだけ作り直せます。ここが気楽なところだと感じました。
PDFの単位はポイント
座標の単位はポイント(pt)です。reportlab の公式ドキュメントには、1ptは1/72インチと書かれています。
A4は210mm×297mmです。ポイントに直すと 約595×842pt になります。手元で reportlab のA4定数を表示すると、595.28×841.89でした。
つまり1mmは約2.83ptです。欄の高さが8mmなら、およそ23pt。この換算を頭に置いておくと、AIが出した数字の見当がつきます。
手順の全体像
流れは7段階です。エンジニアでなくても、順番だけ追えれば十分だと思います。
- PDFに入力欄があるかを確かめる
- 罫線の座標を取り出す
- 文字を置くセルの四角を決める
- 文字だけを載せた重ね打ち用のPDFを作る
- 元のPDFに重ねる
- 画像にして目で確かめる
- 印刷して手書きの部分を書く
僕がやったのは、AIエージェントへの指示と確認です。コードは自分では書いていません。ただ、何をしているかは知っておいた方がよいと思います。ズレたときに、どこを疑えばよいかが分かるからです。
罫線の座標を取る
pdfplumber は、ページ上の図形を一覧で返してくれます。公式の説明では線が .lines で、四角は .rects です。四角を4本の辺にばらして線とまとめた .edges もあります。
import pdfplumber
with pdfplumber.open("form.pdf") as pdf:
page = pdf.pages[0]
print(page.width, page.height)
for r in page.rects:
print(r["x0"], r["x1"], r["top"], r["bottom"])
x0 は左端、x1 は右端です。どちらもページの左端からの距離になります。top と bottom は、ページの上端からの距離です。
申請書の表は、四角ではなく線の集まりで描かれていることもあります。そのときは .lines や .edges を見ます。縦線と横線の交わりから、セルの四角を組み立てていく形です。
ここが一番地味で、一番大事な工程でした。欄の左端・右端・上端・下端の4つの数字が決まれば、あとは機械的に進みます。
重ね打ち用のPDFを作る
次に reportlab で、文字だけのPDFを作ります。日本語を出すにはフォントの登録が要ります。公式ドキュメントに載っているのは UnicodeCIDFont を使う方法です。日本語の書体は2つ挙がっています。明朝の HeiseiMin-W3 とゴシックの HeiseiKakuGo-W5 です。
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
pdfmetrics.registerFont(UnicodeCIDFont("HeiseiKakuGo-W5"))
c = canvas.Canvas("overlay.pdf", pagesize=A4)
c.setFont("HeiseiKakuGo-W5", 10)
H = A4[1] # ページの高さ
x0, x1, bottom = 100, 300, 142 # pdfplumberで取った値
y = H - bottom + 6 # 上下を反転して少し浮かせる
c.drawString(x0 + 4, y, "見本 太郎") # 左寄せ
c.drawRightString(x1 - 4, y, "12,345") # 右寄せ
c.showPage() # 2枚目があれば続けて描く
c.save()
名前と数字はもちろん見本です。ポイントは H - bottom の一行ですね。上からの距離を、下からの距離に直しています。理由は後の章で書きます。
特定の書体をPDFに埋め込みたいときは TTFont も使えます。手元のフォントファイルを指定して登録する方法です。印刷結果の見た目をそろえたいなら、こちらを選ぶのも手かもしれません。
元のPDFに重ねて画像で確かめる
最後に pypdf で2つを重ねます。公式ドキュメントでは「スタンプ」として紹介されている merge_page() です。
from pypdf import PdfReader, PdfWriter
overlay = PdfReader("overlay.pdf")
writer = PdfWriter(clone_from="form.pdf")
for i, page in enumerate(writer.pages):
page.merge_page(overlay.pages[i])
writer.write("filled.pdf")
merge_page() は、何も指定しなければ上に重ねる動きです。over=False を付けると下に敷く形になります。今回は文字を上に載せたいので、そのままで大丈夫です。
できたPDFは、すぐ印刷しません。先に画像にして見ます。pdfplumber の to_image(resolution=150) で、ページをPNGにできます。欄からはみ出していないかを、ここで全部の欄について見ます。
固定費の見直しも、やることは似ています。数字を出して目で確かめ、ズレを直す。通信費もその順番で見ると進めやすいと思います。
座標系は上下が逆
この作業でいちばん間違えやすいのが、座標の向きです。公式ドキュメントで確かめた内容を表にしました。
| 項目 | pdfplumber | reportlab |
|---|---|---|
| 原点 | ページの左上 | ページの左下 |
| 横方向 | x0 x1 は左端からの距離 | x は右へ増える |
| 縦方向 | top bottom は上端からの距離 | y は上へ増える |
| 単位 | ポイント | ポイント(1/72インチ) |
| 変換 | そのまま読む | y = ページの高さ − 上からの距離 |
pdfplumber には y0 と y1 という値もあります。こちらはページの下端からの距離です。なので y0 を使えば、引き算なしで reportlab に渡せます。
ただ、top と y0 を混ぜると混乱します。どちらを使うかを最初に決めて、AIにも伝えておくのがよさそうです。例えば「上からの距離で読んで、描くときに反転する」と決めてしまう形です。
- 文字が用紙の上下反対側に出たら、座標の反転を忘れている可能性が高い
- 横方向はどちらも左端が基準なので、そのまま使える
- 用紙の高さは決め打ちにせず、元のPDFから読んだ値を使うと安心
一度ズレた原因は右端の読み違い
実は一度、文字がズレました。原因は、ある記入欄の右端の座標です。AIが、正しい位置より右にあると読んでいました。
セルの四角は、文字を置く基準になります。右端の数字が違えば、そこを基準にした文字も動きます。こういうズレは、重ねた結果を画像で見るとすぐ分かります。
直し方は単純です。罫線の座標をもう一度読み直して、セルの右端を正しい値に置き換えました。そのうえで重ね打ち用のPDFを作り直しています。
一般論として、申請書の表は線が入り組んでいます。隣の欄の線や外枠の線を、目的の欄の端と取り違えることは起こり得ます。なので、座標は「取れたら終わり」ではありません。画像で確かめる工程とセットだと思っています。
手書きで残した3か所
全部を重ね打ちしたわけではありません。次の3か所は、あえて空けておきました。
| 欄 | 重ね打ち | 理由 |
|---|---|---|
| 氏名・期間・金額などの欄 | した | 打ち間違いを画面で直せる |
| 口座番号 | しなかった | ファイルに残したくない |
| 自署 | しなかった | 本人が書く欄だから |
| 押印 | しなかった | 印刷後に押すものだから |
自署の欄は、本人が自分の手で書くのが一般的な考え方です。印字した名前は、自署の代わりにはなりません。押印も同じで、紙に刷ってから押します。
口座番号は、ルールというより僕の判断です。AIに渡すデータにも、作ったPDFにも残したくありませんでした。数字が少ないので、手で書いても負担は小さいですね。
もう1つ判断したことがあります。開封厳禁の封がされた書類は開けずに同封し、該当欄には「同封の書類のとおり」と書きました。
- 印字でよい欄と自署が必要な欄は、申請先の記載要領で確かめる
- 迷ったら、申請先に聞いてから印刷する
- 訂正の方法も、申請先の決まりに従う
記入値は原本と照合する
AIに任せると、位置合わせに気を取られがちです。ただ、申請書で本当に大事なのは中身の数字の方です。
僕は記入した値を、領収証と明細書のスキャンに照らしました。金額と期間が一致することを、1つずつ確かめています。ここは人の目で見る工程として残しました。
照合の考え方は副業の経費を会計SaaSとメールでAIに照合させている話と同じです。出どころが2つあれば突き合わせる。申請書でも変わりません。
紙をまとめてスキャンする段取りは、ドリルの一括スキャンと仕分けの記事も参考になるかもしれません。
個人情報の扱い
申請書には個人情報が詰まっています。AIに作業させるときは、渡す範囲を先に決めておくのがよいと思います。
- 座標を取るだけなら、空の申請書のPDFだけで足りる
- 記入値は、必要な項目だけを別に渡す
- 口座番号のように、渡さなくても済むものは渡さない
- 作ったPDFや画像は、提出が終わったら置き場所を見直す
特に最後は忘れやすいところです。確認用の画像には、記入済みの内容がそのまま写っています。作業フォルダに残しっぱなしにしない方が安心ですね。
この記事でも、健康保険の名前や金額は書いていません。体験を書くときも渡すデータを絞るときも、考え方は同じだと思っています。
コードを書かない方法もある
ここまで読んで「そこまではやらない」と感じた人もいるかもしれません。その場合は、Adobe Acrobat の「入力と署名」が候補になります。
Adobeの公式ヘルプには、平らなフォームへの入力方法が載っています。Acrobat Reader の Fill & Sign ツールを使う方法です。フォームの好きな位置に文字や記号を置けます。マウスで場所を決めるので、座標を考える必要はありません。
用紙が1〜2枚で、書く欄も少ない。それならこちらの方が早いと思います。AIに座標で打たせる方法が効くのは、欄が多いときです。同じ様式を何度も作るときも向いているのではないでしょうか。
向いている人・向いていない人
向いているのは、次のような人だと思います。
- 手書きの申請書で、同じ内容を何度も書くのが面倒な人
- AIエージェントにファイル操作を任せる環境がすでにある人
- 同じ様式の申請書を、今後も繰り返し出す予定がある人
一方で、向いていないのはこんな場合です。
- 欄が数か所だけで、手書きでも5分で終わる
- 申請先が手書きを求めている、または様式の加工を認めていない
- 画像での確認や原本との照合に、時間を割けない
AIに任せても、確認は人の仕事として残ります。そこを省くなら、手書きの方が安全かもしれません。
- 申請書のPDFに入力欄がないことを確かめたか
- 申請先の記載要領で、印字してよい欄と自署が必要な欄を確かめたか
- 座標の向きを、上からの距離か下からの距離かで統一したか
- 重ねたPDFを画像にして、全部の欄を目で見たか
- 記入値を領収証や明細書の原本と照合したか
- 口座番号など、AIに渡さない項目を決めたか
- 印刷後に手書きする欄を、メモに残してあるか
よくある質問
Q. 入力できないPDFかどうかは、どう見分けますか?
欄をクリックしてカーソルが出なければ、入力欄のないPDFの可能性が高いです。確実なのは pypdf の get_fields() で調べる方法ですね。入力欄がなければ None が返ります。
Q. プログラミングができなくても使えますか?
コードはAIエージェントに書かせる前提です。僕も自分では書いていません。ただ、画像での確認と原本との照合は自分でやる必要があります。
Q. 文字が上下逆の位置に出るのはなぜですか?
座標の原点が違うからです。pdfplumber は左上、reportlab は左下が原点です。ページの高さから上からの距離を引くと、正しい位置になります。
Q. 日本語が文字化けしたり、出なかったりします。
日本語フォントを登録していない可能性があります。reportlab では UnicodeCIDFont で日本語の書体を登録できます。手元のフォントを使うなら TTFont です。
Q. スキャンした紙の申請書でも同じ方法が使えますか?
スキャンしたPDFは、ページ全体が1枚の画像になっていることが多いです。その場合は罫線が図形として入っていないので、pdfplumber では座標を拾えません。画像から欄の位置を測る別の工夫が要ると思います。
Q. 自署欄や押印欄も印字してよいですか?
自署欄は本人が手で書く、というのが一般的な考え方です。押印も印刷後に行います。細かい扱いは申請先によって違うので、記載要領を確かめてみてください。
Q. 印刷したら位置がズレました。
印刷時の拡大縮小が原因かもしれません。用紙に合わせて縮小する設定だと、全体が少し小さくなります。今回は元の用紙と文字を1つのPDFに重ねているので、罫線と文字は一緒に動きます。倍率は実際のサイズにしておくと安心です。
Q. 元のPDFは書き換わりますか?
書き換わりません。元のPDFを読み込んで、重ねた結果を別の名前で保存しています。ズレたら重ね打ち用のPDFだけ作り直せば済みます。
参考にした公式情報
- pdfplumber README(座標と lines / rects / edges の説明)
- ReportLab ユーザーガイド 第2章 Graphics and Text with pdfgen
- ReportLab ユーザーガイド 第3章 Fonts and encodings
- pypdf ドキュメント Adding a Stamp or Watermark to a PDF
- pypdf ドキュメント Interactions with PDF Forms
- Fill in forms, Adobe Acrobat Reader(Adobe ヘルプ)
- Acrobat における PDF フォームの入力と署名(Adobe ヘルプ)
まとめ
記入欄のないPDFでも、罫線の座標さえ取れれば文字は重ねられます。つまずいたのは座標の読み違いでした。読み直せば直るので、画像で確かめる工程は省かない方がよさそうです。
申請書は郵送で出しました。打てるところは打ち、手で書くべきところは手で書く。その線引きを先に決めておくと、作業が落ち着くのではないでしょうか。
手続きの手間を減らせると、家計のほかの見直しにも少し手が回ります。通信費のような毎月の固定費も、その流れで一度見ておくとよいかもしれません。
本記事にはアフィリエイト広告を含みます。当サイトのリンクを経由して商品・サービスへお申し込みいただいた場合、提携先企業から成果報酬を受け取ることがあります。 また、運営者は楽天グループに所属する社員であり、紹介プログラムを通じた報酬を受け取る場合があります。記事内容・評価は広告の有無にかかわらず、運営者の実体験と調査に基づき作成していますが、 上記の関係性をご理解のうえお読みください。詳しくは免責事項・アフィリエイト表記をご覧ください。
楽天モバイルの通信エリア・電波状況について気になることがある方は、公式の電波改善・調査依頼フォームからご相談いただけます。