Pythonに限らずプログラミングでは、再利用しそうなコードはモジュールにしておくと後で本当にラクになります。
今回は定形郵便物の郵便番号枠に郵便番号を印刷するPythonモジュールを作成します。このモジュールがあれば、年賀状でも請求書の郵送でも宛名を印刷するプログラムに再利用できます。
一般に、はがきや封筒に印刷する方法には、1)OSのAPIを利用して直接プリンターで印刷する、2)一度PDFに印刷してからプリンターで印刷する、といった2つの方法が考えられます。
今回は、プリンターに印刷しなくても出来上がりを確認できたり、控えを残しておける点で便利なPDFに印刷してからの方法をPythonで行います。ライブラリには、PythonでのPDF作成の定番ともいえる「ReportLab 」を利用します。
本記事の目次
郵便番号枠の位置(定形郵便物)
日本郵政では、郵便物処理の機械化を推進するために、郵便番号を印刷する位置がマニュアルで決められています。今回は、以下の「郵便番号・バーコードマニュアル」に定められている定形郵便物の郵便番号枠に郵便番号を入力します。
郵便番号・バーコードマニュアル 郵便番号枠 | 日本郵便株式会社
[日本郵便株式会社サイトより]
プログラミングのポイント
今回のプログラミングのポイントは以下の2点を満足できるようにすることです。
1. 郵便番号枠の中に数字をちょうど良く入力する
まず枠にちょうど良く数字を入力するには、「フォントサイズ」と「番号の入力位置」を適切に設定する必要があります。
フォントサイズは、「枠の幅」から「隙間(パディング:padding
)」の2倍(左右分)を差し引いた値で決まりますが、さらに以下のようにそれに適当な係数(FONTSIZE_RATIO
)を掛けます。これにより、枠いっぱいに入力するのか、余裕を持たせるのかを調整できるようにします。
番号の入力位置は「枠の中央」と「番号の中央」を合わせます。そのために、中心を合わせて文字入力できるReportLabのcanvas.drawCentredString(下端中央x,下端中央y,文字)
を利用します。枠の位置は左下の(x, y)
で指定するので、以下のように(x + 枠の幅/2, y + padding)
に合わせます。
2. はがきでも封筒でもモジュールを利用できるようにする
前述の郵便番号枠の位置(定形郵便物)は、右上を基準点にして決められています。一方、PDFでは左下を基準点にして、文字や線を入力します。
ここで、郵便番号枠の位置の座標をPDFの座標に変換できるようにしておけば、どのような郵便物のサイズでも対応できます。それには、以下のように郵便物の幅と高さで、郵便番号枠の位置座標(x, y)
をPDFの座標(x_pdf, y_pdf)
に変換できる変換式を使います。
なお、ReportLabのインストールと基本的な使用例については、以下の記事を参考にしてください。以下の記事ではフォントファイルを読み込んで使用していますが、今回はデフォルトのHelvetica
を使用するのでそのままフォント名を指定するだけです。
モジュールのコード
上記のポイントを踏まえてプログラミングするとコードは以下のようになります。
pdf_postal_code.py
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
from typing import Tuple
# 郵便番号枠の位置とサイズ(定形郵便物)
TEIKEI_RECT = [
# (x, y, 幅, 高さ) >> x,yは枠左下の座標(原点は用紙の右上)
(-55.7 * mm, -20 * mm, 5.7 * mm, 8 * mm),
(-48.7 * mm, -20 * mm, 5.7 * mm, 8 * mm),
(-41.7 * mm, -20 * mm, 5.7 * mm, 8 * mm),
(-34.1 * mm, -20 * mm, 5.7 * mm, 8 * mm),
(-27.3 * mm, -20 * mm, 5.7 * mm, 8 * mm),
(-20.5 * mm, -20 * mm, 5.7 * mm, 8 * mm),
(-13.7 * mm, -20 * mm, 5.7 * mm, 8 * mm)
]
# フォントサイズ係数(適宜調整)
FONTSIZE_RATIO = 2.3
def set_postal_code(postal_canvas: canvas.Canvas,
postal_code: str,
papersize: Tuple[float, float],
padding=1.5 * mm, draw_rect=False):
"""
郵便番号の入力(定形郵便物)
"""
for num, rect in zip(postal_code, TEIKEI_RECT):
# 郵便番号枠の幅でフォントサイズを決める
font_size = rect[2] - padding * 2
postal_canvas.setFont("Helvetica", font_size * FONTSIZE_RATIO)
# 郵便番号枠の位置座標をPDF座標に変換
x_pdf = rect[0] + papersize[0]
y_pdf = rect[1] + papersize[1]
# 郵便番号の入力
postal_canvas.drawCentredString(x_pdf + rect[2] / 2, y_pdf + padding, num)
if draw_rect:
postal_canvas.rect(x_pdf, y_pdf, rect[2], rect[3])
if __name__ == '__main__':
# はがきでテスト
hagaki_size = (100 * mm, 148 * mm)
c = canvas.Canvas("postal_code.pdf", pagesize=hagaki_size)
set_postal_code(c, "1234567", hagaki_size, draw_rect=True)
c.showPage()
c.save()
ここで、関数の引数にはアノテーションを利用しています。モジュールの関数には、アノテーションを用いる使用方法がわかりやすくなり何かと便利です。アノテーションについては以下の記事を参考にしてください。
PDF出力テスト
上記のコードには、if __name__ == '__main__':
ブロックに「はがき(100 x 148mm)」に郵便局「1234567
」を出力してテストを実行できるようにしてあります。
ここでは、draw_rect=True
を設定して枠も出力します。プログラムを実行すると、以下のようにはがきに郵便番号が枠内に出力されているのを確認できます。
次に、「長形3号(120 x 235mm)」に出力してみます。以下のようにサイズを(120 * mm, 235 * mm)
に変更します。
.....
# 以上省略
if __name__ == '__main__':
# 長形3号でテスト
naga3_size = (120 * mm, 235 * mm)
c = canvas.Canvas("postal_code.pdf", pagesize=naga3_size)
set_postal_code(c, "1234567", naga3_size, draw_rect=True)
c.showPage()
c.save()
以下のように、郵便物のサイズを長形3号に変更しても、右上を基準点とした正しい位置に郵便番号が出力されるのが確認できます。
プリンター印刷テスト
今度はdraw_rect=False
にして、枠を出力しないで作成したPDFを実際にプリンターに印刷してみます。
以下のように、はがきの所定の枠内に郵便番号がちゃんと印刷されました。ここで、プリンターは1)用紙サイズ=はがき、2)拡大縮小=100%に設定するのを忘れないでください。
[日本郵便年賀はがきに印刷]
最後に
実際には、はがきや封筒を「手差し」でプリンターに印刷すると、どうしてもズレが生じることがよくあります。そこで、少し余裕を持たせるために今回はpadding=1.5mm
、FONTSIZE_RATIO=2.3
に設定しましたが、プリンターの機種などに応じて適当な値にしてください。
ちなみに、FONTSIZE_RATIO=2.8にすると以下のようにピッチリと余裕が少ない状態になります。
今回はモジュールを作成しただけでしたが、このモジュールを利用したプログラムについては、後日掲載する予定です。