[Python] 既存のPowerpointのテキストをフォーマットを変えずに変更する python-pptx


python-pptxでPowerPointのレポートを操作したい時、事前にテンプレートを用意しておいて文字のフォーマットはそのままで内容の一部を変更したいことがあります。思ったよりも面倒でしたので、こちらの備忘録として残します。

動作を確認するために、以下のようにフォーマットされた文字列のあるPPTファイルの一部(「。。。」)を今日の日付(2021/3/6)で変換するコードを書いてみました。元の文字は影をつけたり、下線をつけたりと、いろいろと装飾してあります。コードを実施される方は"元のファイル.pptx"というファイルを作ってコードと同じディレクトリに保存してください。

なお、既存の文字を置き換える方法は、python-pptxの作者であるScannyさんがこちらで説明していますので、その関数(モジュール)はそのまま利用します。
https://github.com/scanny/python-pptx/issues/285
replace_paragraph_text_retaining_initial_formattingという名前の長い関数がそうです。

私が工夫したポイントとしては、元のファイルの中から該当するparagraphオブジェクトを探すところです。このオブジェクトの関係がいろいろとややこしいのですが、こちらを参考にさせていただきました。この階層のどちらが上だったか、ぜんぜん記憶することができません。。。


import pptx, re
from pptx.util import Cm, Pt, Inches

def replace_paragraph_text_retaining_initial_formatting(paragraph, new_text):
    p = paragraph._p # the lxml element containing the <a:p> paragraph element
    # remove all but the first run
    for idx, run in enumerate(paragraph.runs):
        if idx == 0:
            continue
        p.remove(run._r)
        paragraph.runs[0].text = new_text

prs = pptx.Presentation("元のファイル.pptx")
slide=prs.slides[0] #先頭のページを選択

for shp in slide.shapes:

    if shp.has_text_frame:
        if "日付は" in shp.text: #変更する文字列の一部でマッチさせる
            new_text=re.sub('。。。', '2021年3月6日', shp.text) #置き換え
            replace_paragraph_text_retaining_initial_formatting(shp.text_frame.paragraphs[0], new_text)

prs.save('変更後のファイル.pptx')