PythonでPDFに透かしを入れる方法
Pythonを使用してPDFファイルに透かし(ウォーターマーク)を挿入するには、いくつかのライブラリが利用可能です。ここでは、代表的なライブラリである `reportlab` と `PyMuPDF` を中心に、その実装方法と注意点について解説します。
reportlab を使用した透かし挿入
`reportlab` は、PythonでPDFを生成するための強力なライブラリです。既存のPDFに直接透かしを挿入する機能は限定的ですが、新しいPDFを生成する際に、元となるPDFの内容を背景として配置し、その上に透かしテキストや画像を重ねるというアプローチで実現できます。
基本的な考え方
1. **元PDFの読み込み:** `reportlab` の `pdfreader` モジュールなどを使用して、既存のPDFの各ページの内容を読み込みます。ただし、`reportlab` 自体はPDFの直接的な「読み込み・編集」を主目的としたライブラリではないため、この部分は工夫が必要になる場合があります。より簡単に実現するには、`PyMuPDF` のようなPDF操作に特化したライブラリで元PDFを画像化し、それを `reportlab` で配置するという方法も考えられます。
2. **新しいPDFの生成:** `reportlab` を用いて、新規のPDFドキュメントを作成します。
3. **元PDFページの内容の配置:** 読み込んだ元PDFの各ページの内容を、新規PDFの背景として配置します。これは、`reportlab` の `canvas` オブジェクトの `drawImage` や `drawInlineImage` などのメソッドを使用して、画像として配置する形になります。
4. **透かしの配置:** 新規PDFの前面に、透かしとして表示したいテキストや画像を配置します。テキストの場合は、`canvas.drawString` や `canvas.drawText` を使用し、画像の場合は `canvas.drawImage` を使用します。透かしは、通常、半透明にしたり、ページ全体にわたって配置したりすることが多いため、`alpha` パラメータの調整や、テキストの回転、フォントサイズの指定が重要になります。
実装例(概念)
“`python
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.lib.colors import transparent
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
def add_watermark_with_reportlab(input_pdf_path, output_pdf_path, watermark_text):
# この例は、既存PDFを直接操作するのではなく、
# 既存PDFの内容を画像化してreportlabで再配置するアプローチを想定しています。
# 実際には、pdf_to_images_library を使用してPDFを画像に変換し、
# その画像を reportlab の canvas に描画する処理が必要です。
c = canvas.Canvas(output_pdf_path, pagesize=letter)
# 例: 画像として元PDFのページを読み込む(実際には画像変換ライブラリを使用)
# image_of_page = Image.open(f”page_{page_num}.png”)
# c.drawImage(image_of_page, 0, 0, width=letter[0], height=letter[1])
# 透かしテキストの設定
text_color = transparent # 半透明にするために透明色を使用し、alphaで調整
font_name = “Helvetica”
font_size = 80
rotation_angle = 45
x_position = letter[0] / 2
y_position = letter[1] / 2
# フォントの登録(日本語対応など、必要に応じて)
# pdfmetrics.registerFont(TTFont(‘IPAexMincho’, ‘IPAexMincho.ttf’))
# font_name = ‘IPAexMincho’
c.setFont(font_name, font_size)
c.setFillColorRGB(0.5, 0.5, 0.5, alpha=0.3) # RGBA (0.3は透明度)
# テキストの描画
c.saveState() # 現在の描画状態を保存
c.translate(x_position, y_position) # 回転中心に移動
c.rotate(rotation_angle) # 回転
c.drawCentredString(0, 0, watermark_text) # 中心に描画
c.restoreState() # 保存した描画状態に戻す
# ページネーション(多ページの場合)
# for page_num in range(total_pages):
# … 元PDFのページを画像として描画 …
# … 透かしを描画 …
# c.showPage()
c.save()
# 使用例(概念)
# add_watermark_with_reportlab(“input.pdf”, “output_with_watermark.pdf”, “DRAFT”)
“`
`reportlab` の課題
* **既存PDFの直接編集:** `reportlab` はPDFを「生成」することに長けており、既存のPDFを直接開いて編集する機能は限定的です。そのため、既存PDFの内容を新しいPDFの背景として配置するには、一度PDFを画像に変換するなどの前処理が必要になることが多いです。
* **複雑なページ構造の再現:** 元PDFの複雑なレイアウトやフォント、ベクターグラフィックなどを完全に画像化せずに再現するのは困難な場合があります。
PyMuPDF を使用した透かし挿入
`PyMuPDF` ( `fitz` モジュール) は、MuPDFライブラリのPythonバインディングであり、PDFの作成、編集、レンダリングにおいて非常に高機能で高速なライブラリです。既存のPDFを直接操作し、透かしを効率的に挿入するのに適しています。
基本的な考え方
1. **PDFのオープン:** `fitz.open(input_pdf_path)` で、既存のPDFファイルを開きます。
2. **ページごとの処理:** PDFの各ページに対してループ処理を行います。
3. **透かしオブジェクトの作成:** 透かしとして表示するテキストや画像を用意します。
* **テキスト透かし:** `page.insert_text()` や `page.insert_textbox()` を使用して、指定した位置にテキストを挿入します。テキストの色、フォント、サイズ、透明度(アルファ値)、回転などを細かく設定できます。
* **画像透かし:** `page.insert_image()` を使用して、画像ファイルをページに挿入します。画像のサイズ、位置、透明度などを指定します。
4. **透明度の設定:** 透かしは通常、背景が透けて見えるように半透明にする必要があります。`PyMuPDF` では、`color` パラメータのアルファ値(RGBA形式)で透明度を制御します。例えば、`fitz.RGBA(r, g, b, alpha)` の `alpha` 値を0.0(完全に透明)から1.0(完全に不透明)の間で指定します。
5. **透かしの配置:** 透かしは、通常、ページの中央付近や、ページ全体にわたって配置されます。`page.rect` プロパティでページの矩形情報を取得し、それに基づいて座標を計算します。
6. **PDFの保存:** 変更を加えたPDFを `doc.save(output_pdf_path)` で保存します。
実装例(テキスト透かし)
“`python
import fitz # PyMuPDF
def add_text_watermark(input_pdf_path, output_pdf_path, watermark_text, font_size=50, opacity=0.3, angle=45):
“””
PDFにテキスト透かしを追加します。
Args:
input_pdf_path (str): 入力PDFファイルのパス。
output_pdf_path (str): 出力PDFファイルのパス。
watermark_text (str): 追加する透かしテキスト。
font_size (int): 透かしテキストのフォントサイズ。
opacity (float): 透かしの透明度 (0.0 ~ 1.0)。
angle (int): 透かしテキストの回転角度(度)。
“””
try:
doc = fitz.open(input_pdf_path)
rect = doc[0].rect # 最初のページの矩形情報を取得(全ページで共通と仮定)
for page_num in range(len(doc)):
page = doc.load_page(page_num)
page_rect = page.rect # 現在のページの矩形情報を取得
# 透かしテキストの描画
# ページの中央を計算
mid_x = page_rect.width / 2
mid_y = page_rect.height / 2
# テキストの矩形を作成(中央揃えのため)
# テキストの幅と高さを正確に計算するのは難しい場合があるので、
# ページの中央を基準に、回転と配置で調整します。
# draw_rect = fitz.Rect(0, 0, page_rect.width, page_rect.height) # ページ全体をカバーする矩形
# テキストの挿入
# insert_text の場合、座標はテキストの左下角になります。
# insert_textbox の場合、矩形内での配置方法を指定できます。
# ここでは、回転と中央配置を考慮して insert_text を利用します。
# テキストの描画設定
text_color = fitz.RGBA(0.5, 0.5, 0.5, opacity) # RGBA (0.5, 0.5, 0.5 はグレー、opacity で透明度)
font_name = “helv” # 基本フォント (日本語の場合はフォントファイルの指定が必要)
# 注: 日本語フォントを使用する場合は、fontname にフォントファイルのパスを指定するか、
# システムに登録されているフォント名を指定する必要があります。
# 例: font_name = “C:/Windows/Fonts/msgothic.ttc” (Windows)
# font_name = “/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc” (macOS)
# テキストの座標を計算(回転中心をページ中央に設定)
# ページ中央を基準に、回転させるために canvas の状態を一時的に変更する方が容易です。
# PyMuPDFのinsert_textは canvas とは異なり、直接描画します。
# 回転は、 insert_text の機能ではなく、後述の draw_text_layer などで実現することが多いですが、
# insert_textでも、transform を適用することで回転させることが可能です。
# ここでは、より簡単な方法として、ページの中央を基準とした位置に描画し、
# 後でtransformで回転させます。
# insert_text は、指定した座標をテキストの左下とします。
#中央揃えを模倣するために、テキストの描画領域を想定し、そこからオフセットを計算します。
# テキストの幅を推定 (正確な幅はフォントと文字数に依存)
# ここでは、簡易的にページ幅の半分程度を想定し、中央に配置します。
# より正確には、draw_text_layer を使用して rotate を指定するのが良いでしょう。
# insert_text の代わりに、draw_text_layer を使用して回転させます。
# draw_text_layer は、canvas のような描画コンテキストを返します。
text_layer = page.new_text_layer()
text_layer.set_font(font_name, font_size)
text_layer.set_color(text_color)
# テキストの幅と高さを取得(これは推奨される方法ではありませんが、目安として)
# より正確には、text_layer.get_text_matrix() などを使用しますが、複雑になります。
# ここでは、フォントサイズを基準に簡易的に計算します。
# width = font_size * len(watermark_text) * 0.5 # 簡易的な幅の推定
# height = font_size
# ページ中央に配置するための開始座標を計算
# draw_text_layer は、指定した座標をテキストの左下とします。
# 中央揃えのために、テキストの幅の半分だけ左にずらします。
# ただし、回転もあるため、正確な中心合わせは transform を使うのがベストです。
# transform を利用して回転と移動を行います。
# ページ中央を原点とする回転行列を作成します。
# T = translate(mid_x, mid_y) * rotate(angle) * translate(-mid_x, -mid_y)
# PyMuPDFでは、insert_text の matrix パラメータでこれを実現します。
# シンプルな方法として、テキストをページ中央に配置してから回転させる。
# draw_text_layer を使った直接描画は、transform の適用が少し複雑です。
# より簡単なアプローチ:テキストをページ中央に配置し、回転させる。
# ページ中央を基準に、テキストの左下を計算します。
# text_width = text_layer.get_text_width(watermark_text) # 精確な幅を取得
# canvas の saveState/restoreState のように、PyMuPDF にはその機能はありません。
# insert_text を使用して、matrix を指定して回転させるのが直接的です。
# insert_text に matrix を指定して回転させます。
# matrix = fitz.Matrix(cos(radians(angle)), sin(radians(angle)), -sin(radians(angle)), cos(radians(angle)), x, y)
# x, y は回転後のテキストの左下座標です。
# ページ中央を原点とした回転で、テキストの左下を (mid_x, mid_y) に設定します。
# 回転軸が中央になるように、テキストの幅と高さを考慮してオフセットを調整します。
# ページ中央を原点とし、テキストの左下を (0, 0) として回転させ、
# その後、ページ中央に移動させる transform を作成します。
# ページ中央の座標
center_x = page_rect.width / 2
center_y = page_rect.height / 2
# 回転行列を作成 (angle は度数法)
m_rotate = fitz.Matrix(fitz.math.cos(fitz.math.radians(angle)), fitz.math.sin(fitz.math.radians(angle)),
-fitz.math.sin(fitz.math.radians(angle)), fitz.math.cos(fitz.math.radians(angle)))
# テキストの左下を (0,0) としたときの、中央揃えを考慮したオフセットを計算
# テキストの幅と高さを正確に計算するのは難しいので、ここでは回転中心をテキストの左下として
# ページ中央に移動させる matrix を単純に作ります。
# より正確な中央揃えには、テキストのバウンディングボックスを計算する必要があります。
# ページ中央を原点とし、テキストを回転させ、その後ページ中央に移動させる matrix
# x, y は、回転後のテキストの左下座標をページ座標系で指定します。
# ここでは、テキストの左下をページ中央に配置し、回転させます。
# より正確な中央揃えのためには、テキストの幅と高さを取得し、
# (page_rect.width – text_width)/2 , (page_rect.height – text_height)/2 に配置する必要があります。
# しかし、回転と組み合わせると複雑になります。
# 簡易的な方法:ページ中央にテキストの左下を置き、回転させる。
# page.insert_text(fitz.Point(mid_x, mid_y), watermark_text,
# fontsize=font_size, color=text_color, rotate=angle)
# rotate パラメータは、insert_text には直接ありません。
# draw_text_layer を使って、matrix で回転させるのが最も柔軟です。
# center_matrix = fitz.Matrix(1, 0, 0, 1, mid_x, mid_y) # ページ中央へ移動
# rotation_matrix = fitz.Matrix(fitz.math.cos(fitz.math.radians(angle)), fitz.math.sin(fitz.math.radians(angle)),
# -fitz.math.sin(fitz.math.radians(angle)), fitz.math.cos(fitz.math.radians(angle)))
# final_matrix = center_matrix * rotation_matrix # 回転後に移動 (順番注意)
# PyMuPDF の matrix は、適用順序が重要です。
# 通常、(x, y) -> rotate -> scale -> translate の順になります。
# ページ中央 (mid_x, mid_y) を回転の中心とする場合、
# 1. ページ中央から原点へ移動: translate(-mid_x, -mid_y)
# 2. 回転: rotate(angle)
# 3. 元のページ中央へ移動: translate(mid_x, mid_y)
# これらを合成した matrix を作ります。
# ページ中央を原点とした回転行列
rot_mat = fitz.Matrix(fitz.math.cos(fitz.math.radians(angle)), fitz.math.sin(fitz.math.radians(angle)),
-fitz.math.sin(fitz.math.radians(angle)), fitz.math.cos(fitz.math.radians(angle)))
# ページ中央に移動させる行列
trans_mat = fitz.Matrix(1, 0, 0, 1, mid_x, mid_y)
# 最終的な変換行列: ページ中央に移動 -> 回転 -> 再びページ中央へ戻す(これは不要)
# ページ中央 (mid_x, mid_y) を原点として回転させるには、
# 1. ページ中央から原点へ移動 (-mid_x, -mid_y)
# 2. 回転
# 3. ページ中央へ戻す (+mid_x, +mid_y)
# の順で適用する。
# 1. ページ中央から原点へ移動
mat1 = fitz.Matrix(1, 0, 0, 1, -mid_x, -mid_y)
# 2. 回転
mat2 = rot_mat
# 3. ページ中央へ戻す
mat3 = fitz.Matrix(1, 0, 0, 1, mid_x, mid_y)
# 合成行列: mat3 * mat2 * mat1
final_matrix = mat3 * mat2 * mat1
# テキストの挿入
# insert_text は、matrix を指定できます。
# この matrix は、テキストの座標系からページ座標系への変換を定義します。
# テキストの左下を (0,0) としたときの、最終的なページ上の位置を決定します。
# ここでは、テキストの左下を (0,0) としたまま、matrix を適用します。
# したがって、matrix で計算される x, y が、最終的なテキストの左下の位置になります。
# ページ中央に配置するために、matrix の移動成分を調整します。
# ページ中央の座標を基準に、テキストの左下を計算し、回転させる matrix を作成します。
# テキストの幅と高さを取得します。
text_width = page.get_text_length(watermark_text, font=font_name, fontsize=font_size)
text_height = font_size # 簡易的な高さ
# 中央揃えを考慮したテキストの左下の座標を計算 (回転なしの場合)
base_x = (page_rect.width – text_width) / 2
base_y = (page_rect.height – text_height) / 2
# ページ中央を回転の中心とする matrix を作成し、テキストの左下を base_x, base_y に配置します。
# ページ中央 (mid_x, mid_y) を原点とする座標系での、テキストの左下の位置 (base_x – mid_x, base_y – mid_y)
# を回転させ、元のページ座標系に戻す。
# 変換行列:
# x’ = cos(theta)*x – sin(theta)*y
# y’ = sin(theta)*x + cos(theta)*y
# この x, y は、回転軸を原点とした相対座標です。
# ページ中央 (mid_x, mid_y) を回転の中心とする場合:
# 1. ページ中央から原点へ移動: Translate(-mid_x, -mid_y)
# 2. 回転: Rotate(angle)
# 3. ページ中央へ戻す: Translate(mid_x, mid_y)
# matrix = Translate(mid_x, mid_y) * Rotate(angle) * Translate(-mid_x, -mid_y)
# PyMuPDF の Matrix は、適用順序が重要です。
# x_final = M.a * x_orig + M.b * y_orig + M.c
# y_final = M.d * x_orig + M.e * y_orig + M.f
# ここで、x_orig, y_orig は回転前の座標、x_final, y_final は回転後の座標です。
# Text の左下を (base_x, base_y) としたとき、これをページ中央 (mid_x, mid_y) を中心に回転させます。
# 変換する点 P = (base_x, base_y)
# 1. P – C = (base_x – mid_x, base_y – mid_y) (C は回転中心)
# 2. Rotate(angle) (base_x – mid_x, base_y – mid_y)
# 3. (result_x, result_y) + C = (result_x + mid_x, result_y + mid_y)
# PyMuPDF の Matrix は、applyMatrix(point) のように機能します。
# 変換行列 M は、点の座標 (x, y) を変換して (Mx*x + My*x + M.c, Mx*y + My*y + M.f) にします。
# M.a, M.b, M.d, M.e は回転、スケール、せん断を表し、M.c, M.f は並進を表します。
# ページ中央を原点とした回転行列
rot_mat = fitz.Matrix(fitz.math.cos(fitz.math.radians(angle)), fitz.math.sin(fitz.math.radians(angle)),
-fitz.math.sin(fitz.math.radians(angle)), fitz.math.cos(fitz.math.radians(angle)))
# ページ中央を原点とした相対座標でのテキスト左下
rel_x = base_x – mid_x
rel_y = base_y – mid_y
# 回転を適用
rotated_rel_x = rot_mat.a * rel_x + rot_mat.b * rel_y
rotated_rel_y = rot_mat.d * rel_x + rot_mat.e * rel_y
# ページ中央へ戻す
final_x = rotated_rel_x + mid_x
final_y = rotated_rel_y + mid_y
# テキストを挿入
page.insert_text(fitz.Point(final_x, final_y),
watermark_text,
fontsize=font_size,
color=text_color,
fontname=font_name)
doc.save(output_pdf_path, garbage=4, deflate=True) # garbage=4, deflate=True でファイルサイズを最適化
doc.close()
print(f”透かしが追加されたPDFを ‘{output_pdf_path}’ に保存しました。”)
except Exception as e:
print(f”エラーが発生しました: {e}”)
# 使用例
# input_file = “input.pdf”
# output_file = “output_with_text_watermark.pdf”
# watermark_text = “SAMPLE”
# add_text_watermark(input_file, output_file, watermark_text, opacity=0.2, angle=30)
“`
実装例(画像透かし)
“`python
import fitz # PyMuPDF
def add_image_watermark(input_pdf_path, output_pdf_path, watermark_image_path, opacity=0.3, scale=0.5, x_pos_ratio=0.5, y_pos_ratio=0.5):
“””
PDFに画像透かしを追加します。
Args:
input_pdf_path (str): 入力PDFファイルのパス。
output_pdf_path (str): 出力PDFファイルのパス。
watermark_image_path (str): 追加する透かし画像ファイルのパス (PNG, JPGなど)。
opacity (float): 透かしの透明度 (0.0 ~ 1.0)。
scale (float): 透かし画像のスケール (1.0で元のサイズ)。
x_pos_ratio (float): 画像のX座標の比率 (0.0 ~ 1.0、ページの左端からの割合)。
y_pos_ratio (float): 画像のY座標の比率 (0.0 ~ 1.0、ページの底辺からの割合)。
“””
try:
doc = fitz.open(input_pdf_path)
for page_num in range(len(doc)):
page = doc.load_page(page_num)
page_rect = page.rect # 現在のページの矩形情報を取得
# 透かし画像の挿入
# embed_image は、画像の挿入だけでなく、その後の操作も可能にします。
# embed_image を使うと、PDFの埋め込みリソースとして画像が追加されます。
# insert_image は、直接ピクセルデータを描画します。
# ここでは、insert_image を使用し、必要に応じて matrix で変換します。
# 画像の挿入
# insert_image(rect, filename=None, stream=None, pixmap=None, alpha=1, rotate=0)
# rect: 画像を配置する矩形。
# alpha: 透明度。
# rotate: 回転角度(度)。
# 画像のサイズと配置を計算
img_rect = fitz.Rect(0, 0, 0, 0) # 仮の矩形
# 画像の情報を取得して、スケールに応じたサイズを計算
img_pix = fitz.Pixmap(watermark_image_path)
original_width = img_pix.width
original_height = img_pix.height
# スケールを適用したサイズ
scaled_width = original_width * scale
scaled_height = original_height * scale
# 配置位置を計算 (ページ中央を基準にした比率)
# x_pos_ratio, y_pos_ratio は、ページの左下からの比率として指定します。
# 配置する矩形の左下座標を計算します。
# 中央揃えにする場合:
# left = (page_rect.width – scaled_width) / 2
# bottom = (page_rect.height – scaled_height) / 2
# ここでは、比率指定なので、その比率で計算します。
x0 = page_rect.x0 + (page_rect.width – scaled_width) * x_pos_ratio
y0 = page_rect.y0 + (page_rect.height – scaled_height) * y_pos_ratio
x1 = x0 + scaled_width
y1 = y0 + scaled_height
insert_rect = fitz.Rect(x0, y0, x1, y1)
# 画像の挿入
page.insert_image(insert_rect, filename=watermark_image_path, alpha=opacity)
doc.save(output_pdf_path, garbage=4, deflate=True)
doc.close()
print(f”透かしが追加されたPDFを ‘{output_pdf_path}’ に保存しました。”)
except Exception as e:
print(f”エラーが発生しました: {e}”)
# 使用例
# input_file = “input.pdf”
# output_file = “output_with_image_watermark.pdf”
# watermark_image = “watermark.png”
# add_image_watermark(input_file, output_file, watermark_image, opacity=0.4, scale=0.3, x_pos_ratio=0.5, y_pos_ratio=0.5)
“`
`PyMuPDF` の利点
* **高速性:** MuPDFエンジンの恩恵を受け、非常に高速にPDFの操作ができます。
* **多機能性:** PDFの作成、編集、レンダリング、テキスト抽出、画像抽出など、幅広い機能を提供します。
* **既存PDFの直接操作:** 既存のPDFを直接開いて、ページ単位で透かしの挿入やその他の編集が容易に行えます。
* **柔軟な設定:** テキストや画像の配置、サイズ、色、透明度、回転などを細かく制御できます。
その他の考慮事項
* **フォントの指定:** テキスト透かしの場合、使用するフォントの指定は重要です。特に日本語などの非ラテン文字を含む場合は、対応するフォントファイルを指定する必要があります。`reportlab` では `TTFont` を、`PyMuPDF` では `fontname` パラメータでフォントファイルパスやシステムフォント名を指定します。
* **透明度の設定:** 透かしは、背景のコンテンツが見えるように、通常は半透明で設定されます。RGB値にアルファチャンネル(RGBA)を追加することで、透明度を制御します。`PyMuPDF` では `fitz.RGBA(r, g, b, alpha)` で指定し、`alpha` は 0.0 (透明) から 1.0 (不透明) の範囲で設定します。
* **配置と回転:** 透かしをページのどの位置に、どの角度で配置するかは、PDFの意匠や用途によります。`reportlab` や `PyMuPDF` ともに、座標計算や回転行列を使って柔軟に配置できます。ページの中央に斜めに配置するのが一般的です。
* **エラーハンドリング:** ファイルが存在しない、PDFが破損している、ライブラリのインストールに問題があるなどのエラーが発生する可能性があります。`try-except` ブロックを使用して、これらのエラーを適切に処理することが重要です。
* **パフォーマンス:** 大量のPDFファイルに透かしを挿入する場合、処理速度は重要な要素となります。`PyMuPDF` は一般的に高速ですが、処理内容によっては工夫が必要です。
* **ファイルサイズ:** 透かしを追加すると、PDFのファイルサイズが増加する可能性があります。特に画像透かしを使用する場合や、PDFの圧縮を適切に行わない場合に顕著になります。`PyMuPDF` の `doc.save()` メソッドでは、`garbage=4, deflate=True` などのオプションでファイルサイズを最適化できます。
まとめ
PythonでPDFに透かしを入れるには、`reportlab` や `PyMuPDF` といったライブラリが利用できます。
* `reportlab` は、PDFを「生成」するのに優れており、既存PDFを画像化して再構成するアプローチで透かしを挿入できます。
* `PyMuPDF` は、既存PDFを直接編集するのに非常に強力で、高速かつ柔軟に透かし(テキスト、画像)を挿入できます。
どちらのライブラリを選択するかは、実現したい要件(新規PDF作成か既存PDF編集か)、既存のPDFの複雑さ、パフォーマンス要件などによって異なります。一般的に、既存のPDFに透かしを効率的に追加したい場合は、`PyMuPDF` が第一候補となるでしょう。
