過去に、Pythonのreportlabという外部ライブラリを使ってPDFを出力する方法をご紹介しました。
※過去の記事はこちらを参照ください。
前回はデータを表形式に出力するだけのシンプルなPDFだったので、今回はPDF上に画像を追加する方法を記事にしたいと思います。
会社のロゴ画像を挿入したり、画像挿入する必要がある方はぜひ参考にしてみてください!
目次
動作環境
MacBook pro
Anaconda3(MiniForge3)
Python 3.9.10
reportlab 3.6.12(無料版)
Pillow 9.0.1
画像処理を扱うライブラリ「pillow」をインストール
今回は画像を扱うため、以下のコマンドから「Pillow」をインストールします。
========================================== $ conda install -c anaconda pillow ==========================================
Pillowは「Pythonで画像処理を行うためのライブラリ」です。
画像処理を行うライブラリといえば有名なのがOpenCVです。
しかし、PillowはOpenCVよりもシンプルに扱うことができるので、トリミングやカラーの変更など単純な画像処理をするだけであればPillowがおすすめです。
PDFに画像を挿入するときは、drawInlineImage() を使う
画像はdrawInlineImageに引数として、画像のパス・位置(横縦)・画像のサイズ(横縦)を与えて埋め込みます。
以下のコードを paper.save() の前に追記します。
==================================================== # 挿入したいファイルのパス img = Image.open('image01.jpg') # 画像の埋め込み(画像ファイルのパス, 横位置, 縦位置, 画像横サイズ, 画像縦サイズ) pdf_canvas.drawInlineImage(img, 50, 750, 100, 60) ====================================================
サンプルコードに上記のコードを追記して、PDFを出力してみます。
すると、以下のように画像が挿入されました!
PDFに背景透過でPNG画像を挿入する場合
PNG画像を挿入する場合は、drawInlineImage() ではなく、drawImage() を利用して[mask]オプションを有効にします。
また、drawImage() で利用するImageパラメータを、下のコードのように[open]メソッドなどで作成する場合にエラーになるので注意が必要です!
※ 先ほど紹介した drawInlineImage() には[mask]オプションはありません。
## エラーになるため注意 img = Image.open('image01.png')
その場合は、以下のように ReportLabの ImageReader() を利用して、Imageオブジェクトを作成するとエラーを改善することができます。
from reportlab.lib.utils import ImageReader ## 挿入したいファイルのパス ## img = ImageReader('image01.png') ## 画像の埋め込み(画像ファイルのパス, 横位置, 縦位置, 画像横サイズ, 画像縦サイズ) pdf_canvas.drawImage(img, 50, 750, 100, 60, preserveAspectRatio=True, mask='auto')
サンプルコード
前回の記事に掲載したサンプルコードに、画像出力するコードを追加しています。
※ 背景透過(PNG画像)の場合のコードはコメント化しています。
# -*- coding: utf-8 -*-
import pandas as pd
import datetime
import math
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.lib.pagesizes import A4, portrait
from reportlab.platypus import Table, TableStyle
from reportlab.lib.units import mm
from reportlab.lib import colors
from reportlab.platypus.frames import Frame
from PIL import Image
from reportlab.lib.utils import ImageReader
t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')
now = datetime.datetime.now(JST)
d = now.strftime('%Y%m%d') #YYYYMMDD形式に書式化
ymd = now.strftime('%Y年%m月%d日') #YYYYMM形式に書式化
# 初期設定
def make(pdf_data):
filename="気象データ" #ファイル名
pdf_canvas = set_info(filename) #キャンバス名
print_string(pdf_canvas, pdf_data)
pdf_canvas.save() # 保存
def set_info(filename):
pdf_canvas = canvas.Canvas("./{0}_{1}.pdf".format(d,filename)) #保存先
## JPGの場合 ##########################################################
## 挿入したいファイルのパス ##
img = Image.open('image01.jpg')
## 画像の埋め込み(画像ファイルのパス, 横位置, 縦位置, 画像横サイズ, 画像縦サイズ)
pdf_canvas.drawInlineImage(img, 50, 750, 100, 60)
## 背景透過(PNG画像)の場合 ################################################################
# img = ImageReader('image01.png')
# pdf_canvas.drawImage(img, 50, 750, 100, 60, preserveAspectRatio=True, mask='auto')
########################################################################################
pdf_canvas.setAuthor("") #作者
pdf_canvas.setTitle("") #表題
pdf_canvas.setSubject("")#件名
return pdf_canvas
#フォーマット作成
def print_string(pdf_canvas, pdf_data):
maxlow = 45 #行数
pages = (len(pdf_data) + 1) / maxlow #ページ数
num = math.ceil(pages)
for i in range(0, len(pdf_data), 44):
tmp_pdf_data = pdf_data[i: i+44]
pdfmetrics.registerFont(UnicodeCIDFont('HeiseiKakuGo-W5')) # フォント
width, height = A4 # 用紙サイズ
# (1)タイトル
font_size = 24 # フォントサイズ
pdf_canvas.setFont('HeiseiKakuGo-W5', font_size)
pdf_canvas.drawString(230, 770, '気象実績データ') # 書き出し(横位置, 縦位置, 文字)
# (2)作成日
font_size = 7
pdf_canvas.setFont('HeiseiKakuGo-W5', font_size)
pdf_canvas.drawString(465, 770, f'作成日: {ymd}')
# (3) 気象データ取得地点
target_date = pdf_data[0][0]
prefecture = pdf_data[0][1]
header_data = [
['取得日 ',target_date],
['都道府県 ',prefecture],
]
table = Table(header_data, colWidths=(40*mm,60*mm), rowHeights=5*mm)#tableの大きさ
table.setStyle(TableStyle([#tableの装飾
('FONT', (0, 0), (-1, -1), 'HeiseiKakuGo-W5', font_size),#フォントサイズ
('BOX', (0, 0), (-1, -1), 1, colors.black),#罫線
('INNERGRID', (0, 0), (-1, -1), 1, colors.black),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),#フォント位置
]))
table.wrapOn(pdf_canvas, 20*mm, 250*mm)#table位置
table.drawOn(pdf_canvas, 20*mm, 250*mm)
# (4)地点ごとの気象データ
detail_data = [
['地点', '平均気温(℃)', '最高気温(℃)','最低気温(℃)','降水量合計(mm)','降水量合計(mm)','天気(昼)'],
]
temp_data = [row[2:9] for row in tmp_pdf_data]
detail_data.extend(temp_data)
if len(detail_data) <= maxlow:
index_no = maxlow - len(detail_data)
for idx in range(index_no):
detail_data.append([" ", " ", " ", " ", " ", " ", " "])
table = Table(detail_data, colWidths=(25*mm,25*mm,25*mm,25*mm,25*mm,25*mm,25*mm), rowHeights=5*mm)
table.setStyle(TableStyle([
('FONT', (0, 0), (-1, -1), 'HeiseiKakuGo-W5', font_size),
('BOX', (0, 0), (-1, -(index_no + 1)), 1, colors.black),#罫線
('INNERGRID', (0, 0), (-1, -(index_no + 1)), 1, colors.black),#罫線
('ALIGN', (1, 0), (6, -1), 'RIGHT'),#右揃え
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),#フォント位置
]))
table.wrapOn(pdf_canvas, 20*mm, 20*mm)
table.drawOn(pdf_canvas, 20*mm, 20*mm)
# # 1枚目終了
pdf_canvas.showPage()
# 作成
if __name__ == '__main__':
#気象データ読み込み
input_fileName = "東京天気.csv"
df_csv = pd.read_csv(filepath_or_buffer=input_fileName, encoding='shift_jis')
pdf_data = df_csv.values.tolist()
make(pdf_data)
まとめ
- 画像挿入には drawInlineImage()を使う。ただし背景透過しないので注意!
- 背景透過でPNG画像を挿入する場合は、drawImage()を使う
API ARIMA AutoML Bard BigQuery Bing ChatGPT Cloud Endpoints Cloud Storage DWH EBPM GAS Generative AI Google Apps Script Google Cloud Google Form Google Workspace IT組織 Outlook PaLM PDF Python ReportLab selenium Statsmodels STL VertexAI Vertex Forecast スクラッチ セミナー ソトミル トレンド分析 トレーニング バッチ予測 世界は女性とデジタルが救う 女性活躍 技術 時系列データ分析 業務効率化 機械学習 特徴量エンジニアリング 生成AI 自動化 評価指標 需要予測