非IT企業に勤める中年サラリーマンのIT日記

非IT企業でしかもITとは全く関係ない部署にいる中年エンジニア。唯一の趣味がプログラミングという”自称”プログラマー。

【Python】python-pptxでPowerPointスライドの複製とページ挿入

   

PythonでPowerPointを操作する「python-pptx」は強力なライブラリですが、既存スライドを複製して任意のページへ挿入する高レベルAPIは提供されていません。標準機能では新規スライドが必ず末尾に追加されるため、動的なドキュメント生成において大きな制約となります。この課題を解決するために内部のXML構造を直接操作し、スライドの完全複製と自由なページ順序の入れ替えを一本道のコードで実現する技術を解説します。

from copy import deepcopy
from pathlib import Path
from pptx import Presentation

# ==========================================
# 準備:ファイルの読み込み
# ==========================================
# ※ 実行する際は、スクリプトと同じフォルダに「format2.pptx」を用意してください。
template_path = Path("format.pptx")
if not template_path.exists():
    raise FileNotFoundError(f"テンプレートファイルが見つかりません: {template_path}")

prs = Presentation(str(template_path))

# ==========================================
# 設定:どのページを、どこにコピーするか
# ==========================================
# python-pptxのインデックスは「0」から始まります(0=1ページ目、1=2ページ目...)
# 例として「3ページ目(index=2)」をコピー元とし、「4ページ目(index=3)」の直後に挿入します。
source_index = 2  # コピー元のページ(3ページ目)
after_index = 3   # このページの直後に挿入したい(4ページ目)

# ------------------------------------------
# 1. コピー元スライドのオブジェクトを取得
# ------------------------------------------
source_slide = prs.slides[source_index]

# ------------------------------------------
# 2. 新しいスライド用の「レイアウト」を決定
# ------------------------------------------
# 複製スライドの土台にするため、余計な文字枠(プレースホルダー)がない
# 「白紙(空白)レイアウト」をテンプレート内から自動探索します。
blank_layout = None
for layout in prs.slide_layouts:
    if len(layout.placeholders) == 0:
        blank_layout = layout
        break

# もし白紙レイアウトがマスターに登録されていなければ、安全策として先頭のレイアウトを使用
if blank_layout is None:
    blank_layout = prs.slide_layouts[0]

# ------------------------------------------
# 3. いったん「末尾」に新しいスライドを追加
# ------------------------------------------
# python-pptxの仕様上、スライドは常に「末尾」にしか追加できません。
new_slide = prs.slides.add_slide(blank_layout)

# ------------------------------------------
# 4. 新スライドに自動生成された不要な枠を削除
# ------------------------------------------
# 選択したレイアウトに文字入力枠などが残っていた場合、
# コピー元と重なって邪魔になるため、XMLレベルで一度すべて削除(クリア)します。
for shape in list(new_slide.shapes):
    if getattr(shape, "is_placeholder", False):
        sp = shape.element
        sp.getparent().remove(sp)

# ------------------------------------------
# 5. 元スライドの図形(見た目)を完全コピー
# ------------------------------------------
# コピー元スライドにある「すべての図形や表(shapes)」のXML構造を、
# ディープコピー(完全複製)して新しいスライドの構造(_spTree)へ流し込みます。
for shape in source_slide.shapes:
    # shape.element(XML要素)を丸ごと複製
    el = deepcopy(shape.element)
    # 新しいスライドの描画ツリー(extLstの手前)に挿入
    new_slide.shapes._spTree.insert_element_before(el, "p:extLst")

# ------------------------------------------
# 6. スライドの「ページ順序」を入れ替える(重要)
# ------------------------------------------
# この時点では、中身はコピーできてもページ自体は「末尾」にあります。
# PowerPoint内部のページ順序リスト(_sldIdLst)を直接書き換えて、指定位置へ移動させます。

sldIdLst = prs.slides._sldIdLst

# 末尾(一番最後)にある、今作った新しいスライドのIDを取り出す
target_sldId = sldIdLst[-1]

# いったんリストの末尾から切り離す
sldIdLst.remove(target_sldId)

# 「after_indexの次の位置」を計算し、そこへIDを割り込ませる
# (例:4ページ目の後ろに入れたい場合、インデックスは 3 + 1 = 4 になる)
insert_pos = after_index + 1
sldIdLst.insert(insert_pos, target_sldId)

# ==========================================
# 完了:別名で保存
# ==========================================
output_path = Path("generated_output.pptx")
prs.save(str(output_path))
print(f"スライドの複製・挿入が完了しました。保存先: {output_path}")
 

 

 

スポンサーリンク

 - Python