mcpField
L
libreoffice-impress

libreoffice-impress

github.com/dfk1352ドキュメント7107 回インストール3 日前に更新
リポジトリ

libreoffice-impress について

libreoffice-impressは、UNO経由でLibreOffice Impressのプレゼンテーションを操作するPythonモジュールを提供します。構造化ターゲットによるスライド編集に加え、書き出しやスナップショットでの表示確認に対応します。

主な機能

  1. 01プレゼンテーションの作成と書き出し
  2. 02構造化ターゲットによるスライド編集
  3. 03テキスト、リスト、図形、メディアのパッチ
  4. 04表とグラフの更新
  5. 05スライドのスナップショット取得

活用シーン

  1. 01デッキに一連の編集を適用
  2. 02スライドのテキストと書式を更新
  3. 03プレゼンテーションをPDFまたはPPTXに書き出し
  4. 04編集後にスライドのレイアウトを確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dfk1352/libreoffice-skills --skill libreoffice-impress

インストール後、Claude Code で/libreoffice-impressを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
troubleshooting.md
scripts/
colors.py
constants.py
exceptions.py
patch_base.py
session.py
uno_bridge.py
scripts/impress/
__init__.py
core.py
exceptions.py
patch.py
session.py
snapshot.py
targets.py

詳細

LibreOffice Impress

UNO を使用した Impress プレゼンテーションの作業には、同梱の impress モジュールを使用します。 すべてのパスは絶対パスで指定してください。同梱モジュールは、この スキルディレクトリ内の scripts/ にあるため、PYTHONPATH=<skill_base_dir>/scripts を設定してください。 セットアップまたは実行時に問題が発生した場合は、references/troubleshooting.md を確認してください。

API サーフェス

# Non-session utilities
create_presentation(path)
get_slide_count(path)
export_presentation(path, output_path, export_format)   # formats: "pdf", "pptx"
snapshot_slide(doc_path, slide_index, output_path, width=1280, height=720)

# Session (primary editing API)
ImpressSession(path) -> context manager

ImpressSession methods:
  get_slide_count() -> int
  get_slide_inventory(target: ImpressTarget) -> dict[str, object]
  add_slide(index=None, layout="BLANK")
  delete_slide(target: ImpressTarget)
  move_slide(target: ImpressTarget, to_index)
  duplicate_slide(target: ImpressTarget)
  delete_item(target: ImpressTarget)
  read_text(target: ImpressTarget) -> str
  insert_text(text, target: ImpressTarget | None = None)
  replace_text(target: ImpressTarget, new_text)
  format_text(target: ImpressTarget, formatting: TextFormatting)
  insert_list(items: list[ListItem], ordered: bool, target: ImpressTarget | None = None)
  replace_list(target: ImpressTarget, items: list[ListItem], ordered: bool | None = None)
  insert_text_box(slide: ImpressTarget, text, placement: ShapePlacement, name=None)
  insert_shape(slide: ImpressTarget, shape_type, placement: ShapePlacement, fill_color=None, line_color=None, name=None)
  insert_image(slide: ImpressTarget, image_path, placement: ShapePlacement, name=None)
  replace_image(target: ImpressTarget, image_path=None, placement: ShapePlacement | None = None)
  insert_table(slide: ImpressTarget, rows, cols, placement: ShapePlacement, data=None, name=None)
  update_table(target: ImpressTarget, data)
  insert_chart(slide: ImpressTarget, chart_type, data, placement: ShapePlacement, title=None, name=None)
  update_chart(target: ImpressTarget, chart_type=None, data=None, placement: ShapePlacement | None = None, title=None)
  insert_media(slide: ImpressTarget, media_path, placement: ShapePlacement, name=None)
  replace_media(target: ImpressTarget, media_path=None, placement: ShapePlacement | None = None)
  set_notes(target: ImpressTarget, text)
  get_notes(target: ImpressTarget) -> str
  list_master_pages() -> list[str]
  apply_master_page(target: ImpressTarget, master_target: ImpressTarget)
  set_master_background(target: ImpressTarget, color)
  import_master_page(template_path) -> MasterPageImportResult
  patch(patch_text, mode="atomic") -> PatchApplyResult
  export(output_path, export_format)
  reset()
  close(save=True)

# Standalone patch utility
patch(path, patch_text, mode="atomic") -> PatchApplyResult

構造化ターゲット:ImpressTarget

from impress import ImpressTarget

ImpressTarget(
    kind=(
        "slide" | "shape" | "text" | "table" | "chart" |
        "media" | "notes" | "master_page" | "list" | "insertion" | "image"
    ),
    slide_index=None,
    shape_name=None,
    shape_index=None,
    shape_type=None,
    placeholder=None,
    text=None,
    after=None,
    before=None,
    occurrence=None,
    master_name=None,
)
ターゲットの種類
種類対応フィールド用途
slideslide_indexスライドの一覧表示、削除、移動、複製、マスターページの適用
shapeslide_index と shape_name または shape_index汎用図形を読み取る/削除する
imageslide_index と shape_name または shape_index画像を置換する/削除する
tableslide_index と shape_name または shape_index表を更新する/削除する
chartslide_index と shape_name または shape_indexグラフを更新する/削除する
mediaslide_index と shape_name または shape_indexメディアオブジェクトを置換する/削除する
textスライドスコープのセレクターと text、after、before、occurrenceテキストを読み取る、置換する、削除する、または書式設定する
listスライドスコープのテキストセレクター構造化されたリストブロックを置換する/削除する
insertionスライドスコープのセレクターとアンカーテキストまたはリストを挿入する
notesslide_index と任意のテキスト範囲発表者ノートを読み取る/置換する
master_pagemaster_name1つのマスターページを特定する
解決ルール
  • スライドインデックスは 0 始まりです。
  • shape_name と shape_index は同時に指定できません。
  • placeholder ターゲットでは、title、body、subtitle などの明示的な値を指定できます。
  • 解決済みのテキストを含むオブジェクト内でテキストまたはリストの解決範囲を絞り込むには、after と before を使用します。
  • オブジェクトターゲットでは shape_name を優先してください。スライドの順序が安定している場合に限り、shape_index を使用します。
  • delete_item() は、スライド以外の削除ターゲット(text、notes、list、shape、image、table、chart、media)を受け付けます。
  • apply_master_page() はスライド単位のターゲット指定に対応しています。UNO で 1 枚のスライドに別のマスターを割り当て、隣接するスライドのマスターが以前のままであることを確認して検証済みです。

書式設定ペイロード:TextFormatting

from impress import TextFormatting

TextFormatting(
    bold=None,
    italic=None,
    underline=None,
    font_name=None,
    font_size=None,
    color=None,          # named color or integer
    align=None,          # "left" | "center" | "right" | "justify" | "start" | "end"
)

注意:

  • 書式設定フィールドを 1 つ以上設定する必要があります。
  • 色には、色名または 0xRRGGBB 整数を指定できます。
  • 段落の配置は、文字のスタイル設定と同じ書式設定ペイロードで適用します。

ジオメトリペイロード:ShapePlacement

from impress import ShapePlacement

ShapePlacement(x_cm=1.0, y_cm=2.0, width_cm=8.0, height_cm=4.0)
  • ジオメトリの値はセンチメートル単位です。
  • 幅と高さは正の値である必要があります。

リスト項目

from impress import ListItem

ListItem(text="Confirm scope", level=0)
  • level は 0 始まりのネストレベルです。
  • ネストレベルを飛ばすことはできません。
  • リストの編集は構造に基づいて行います。手動で - を追加しないでください。
  • ヘッドレスでのスナップショット描画では、リストのメタデータが正しくても、箇条書きが視覚的に表示されない場合があります。

パッチ DSL

patch() または session.patch() を使用して、順序付きの操作を適用します。

[operation]
type = replace_text
target.kind = text
target.slide_index = 2
target.placeholder = body
target.text = Quarterly revenue rose 18%
new_text = Quarterly revenue rose 21%

[operation]
type = insert_list
target.kind = insertion
target.slide_index = 2
target.shape_name = Agenda Box
target.after = Action Items
list.ordered = false
items <<JSON
[
  {"text": "Confirm scope", "level": 0},
  {"text": "Review outputs", "level": 0},
  {"text": "Update notes", "level": 1}
]
JSON

[operation]
type = delete_item
target.kind = chart
target.slide_index = 4
target.shape_name = Disposable Chart
対応する操作タイプ
  • add_slide
  • delete_slide
  • move_slide
  • duplicate_slide
  • insert_text
  • replace_text
  • format_text
  • insert_list
  • replace_list
  • insert_text_box
  • insert_shape
  • delete_item
  • insert_image
  • replace_image
  • insert_table
  • update_table
  • insert_chart
  • update_chart
  • insert_media
  • replace_media
  • set_notes
  • apply_master_page
  • set_master_background
パッチ値のルール
  • 主なターゲットには target.* フィールドを使用します。
  • apply_master_page には master.* フィールドを使用します。
  • TextFormatting には format.* フィールドを使用します。
  • ShapePlacement には placement.* フィールドを使用します。
  • リスト操作には list.ordered と JSON の items を使用します。
  • items と data は有効な JSON である必要があります。
  • 複数行のテキストまたは JSON には、<<TAG ... TAG 形式のヒアドキュメントブロックを使用できます。
モード
  • atomic は最初の失敗時に停止し、ファイルを元のバイト列に復元して、何も保存しません。
  • best_effort は先行する成功済みの操作を維持し、後続の失敗を記録します。
  • リスト操作は操作の途中で保存やリセットを行わなくなったため、アトミックなロールバックに正しく参加します。

PatchApplyResult のフィールド:

  • mode
  • overall_status = "ok" | "partial" | "failed"
  • operations = PatchOperationResult のリスト
  • document_persisted

単独の patch(path, ...) では、document_persisted は変更がディスクに保存されたことを意味します。session.patch(...) では、パッチによって現在の開いているセッション状態に変更が正常に適用されたことを意味します。

例:セッションでスライドを編集する

from pathlib import Path

from impress import (
    ImpressSession,
    ImpressTarget,
    ListItem,
    ShapePlacement,
    TextFormatting,
)
from impress.core import create_presentation

output = str(Path("test-output/demo.odp").resolve())
create_presentation(output)

with ImpressSession(output) as session:
    session.add_slide(layout="TITLE_AND_CONTENT")
    session.replace_text(
        ImpressTarget(kind="text", slide_index=1, placeholder="title"),
        "Executive Summary",
    )
    session.replace_text(
        ImpressTarget(kind="text", slide_index=1, placeholder="body"),
        "Quarterly revenue rose 18%.",
    )
    session.insert_text_box(
        ImpressTarget(kind="slide", slide_index=1),
        "Action Items",
        ShapePlacement(1.0, 5.0, 10.0, 4.0),
        name="Agenda Box",
    )
    session.insert_list(
        [
            ListItem(text="Confirm scope", level=0),
            ListItem(text="Review output", level=0),
            ListItem(text="Update packaging", level=1),
        ],
        ordered=False,
        target=ImpressTarget(
            kind="insertion",
            slide_index=1,
            shape_name="Agenda Box",
            after="Action Items",
        ),
    )
    session.format_text(
        ImpressTarget(
            kind="text",
            slide_index=1,
            placeholder="body",
            text="Quarterly revenue rose 18%.",
        ),
        TextFormatting(bold=True, align="center"),
    )

例:既存のプレゼンテーションにパッチを適用する

from impress import patch

result = patch(
    "/abs/path/demo.odp",
    """
    [operation]
    type = replace_text
    target.kind = text
    target.slide_index = 1
    target.placeholder = body
    new_text = Quarterly revenue rose 21%.

    [operation]
    type = insert_media
    target.kind = slide
    target.slide_index = 1
    media_path = /abs/path/demo.wav
    placement.x_cm = 1.0
    placement.y_cm = 9.0
    placement.width_cm = 5.0
    placement.height_cm = 3.0
    name = Demo Media

    [operation]
    type = delete_item
    target.kind = media
    target.slide_index = 1
    target.shape_name = Demo Media
    """,
    mode="best_effort",
)

print(result.overall_status)

スナップショット

from pathlib import Path
from impress import snapshot_slide

result = snapshot_slide(doc_path, 0, "/tmp/slide1.png")
print(result.file_path, result.width, result.height)
Path(result.file_path).unlink(missing_ok=True)   # clean up used snapshots

テキストの編集、マスターページの変更、表やグラフの配置など、視覚的な操作後のスライドレイアウトを確認するには、スナップショットを使用します。

  • TITLE_AND_CONTENT などのレイアウトでは、add_slide() の直後から、書き込み可能なタイトル/本文プレースホルダーを使用できます。
  • get_slide_inventory() には名前が空の図形が含まれる場合があります。返されたインベントリーを使用して、スライドで利用できる永続化済みの識別子を確認してください。
  • ユーザーが指定した図形名は、永続化されたインベントリー内で LibreOffice によって正規化されることがあります。たとえば、スペースがアンダースコアに置き換えられます。

よくある間違い

  • 相対パスを渡すこと。UNO を使用する Impress API では、絶対ファイルパスが必要です。
  • スライドのインデックスに 1 始まりの値を使用すること。
  • より長いフレーズを使用できる場合に、壊れやすい単語 1 つだけのテキストアンカーを使用すること。
  • LibreOffice ネイティブのスライド複製後も図形名が完全に一致すると期待すること。UNO によって、Name 1 のように複製名が変更される場合があります。
  • items または data のパッチフィールドに、不正な形式の JSON を指定すること。
  • 確認後に、キャプチャしたスナップショットをクリーンアップし忘れること。
  • session.close() の後にセッションメソッドを呼び出すこと。
  • 段落の配置オプション "start" と "end" には、LibreOffice 26.2 以降が必要です。

コメント