python-pptx を使って既存のノート欄の装飾(太字や箇条書きなど)を保存したまま編集する方法を紹介します。
pptx-python とは
Python を使用してパワーポイントファイル (pptx) を編集できるライブラリとして python-pptx があります。ファイル全体の Presentation オブジェクトであったり、各スライドの Slide オブジェクト、図形や画像やプレースホルダーといった Shape オブジェクト、そしてノート欄の Note オブジェクトなどが定義されていて、そんなに凝ったことでなければ対応可能です。
例えば、既存の pptx ファイルを読み込んで、ページ数を出力するには以下のようなコードを記述します。
import pptx
p = pptx.Presentation("./sample.pptx")
print(len(p.slides))
また、全てのスライドのノート欄を抽出してテキストファイルとして保存するには、以下のようなコードを記述します。
import pptx
p = pptx.Presentation("./sample.pptx")
with open("./sample.txt", 'w') as out:
for slide in p.slides:
out.write(slide.notes_slide.notes_text_frame.text)
out.write("\n\n")
装飾(太字や箇条書きなど)を保存したままノート欄を編集する
問題点の確認
ノート欄を抽出するだけなら上述した slide.notes_slide.notes_text_frame.text
で十分なのですが、これを上書きしようとすると、少し残念なことになります。
例えば以下のようなコードを準備して、
import pptx
p = pptx.Presentation("./sample.pptx")
slide1 = p.slides[0]
slide1.notes_slide.notes_text_frame.text += "追記テキスト"
p.save("./sample2.pptx")
プログラムを実行すると以下のようになります。
元ファイル (sample.pptx) にあった太字や箇条書きといった装飾がなくなっています。
解決策
ノート欄のプレースホルダーを編集することで対応します。以下は追記の例です。
import pptx
from pptx.enum.shapes import PP_PLACEHOLDER
p = pptx.Presentation("./sample.pptx")
slide1 = p.slides[0]
def get_body_ph(placeholders):
return list(filter(lambda ph: ph.element.ph_type == PP_PLACEHOLDER.BODY, placeholders))[0]
note_body = get_body_ph(slide1.notes_slide.placeholders).element.txBody
new_p = note_body.add_p()
new_p.append_text("追記テキスト")
p.save("./sample2.pptx")
解説
ノート欄は複数のプレースホルダーで構成されています。まずはどのようなプレースホルダーがあるのか、先程の sample.pptx で確認してみます。
import pptx
p = pptx.Presentation("./sample.pptx")
slide1 = p.slides[0]
for ph in slide1.notes_slide.placeholders:
print("--------------")
print(ph.element.ph_type)
print(ph.text)
# --------------
# SLIDE_IMAGE (101)
#
# --------------
# BODY (2)
# これは サンプル です。
# サンプル1
# サンプル2
# サンプル3
# --------------
# SLIDE_NUMBER (13)
# 1
少し補足をすると、そもそも pptx は内部的には xml で構造化されていて、python-pptx においては .element でその要素を取得できます。そして、xml 中にそのプレースホルダーの種類が定義されている場合は、ph_type にてそれを取得できます。結果を見てみると、どうやら BODY タイプが、追記すべきプレースホルダーのようです。
ということで、BODY プレースホルダーを取得する関数を定義します。
from pptx.enum.shapes import PP_PLACEHOLDER
def get_body_ph(placeholders):
return list(filter(lambda ph: ph.element.ph_type == PP_PLACEHOLDER.BODY, placeholders))[0]
そして、この関数で得られたオブジェクトの element を string として出力してみると、実際のテキストは <p:txBody> 要素以下に配置されていることがわかるので、この部分だけ取得します。
note_body = get_body_ph(slide1.notes_slide.placeholders).element.txBody
あとは、この要素に対していろいろ操作をすることで、やりたいことが実現できます。既に存在する段落要素 <a:p> を削除すれば消えますし、新たな段落要素を追加してあげて、その段落要素に追記したいテキストをセットすれば、既存の段落には一切触れずに追記ができます。以下は、追記の例です。
new_p = note_body.add_p()
new_p.append_text("追記テキスト")
補足
そのオブジェクトの属性を出力:
def _print_attributes(obj):
print(type(obj))
for m in dir(obj):
print(m)
xml を文字列として出力:
from lxml import etree
def _print_xml(obj):
print(etree.tostring(obj).decode('utf-8'))