PowerPointファイルをAIフレンドリーなMarkdownに変換するスクリプトを書いて試してみました。
社内やプロジェクトで蓄積されたPowerPoint資料をAIから参照させたい場面があります。しかしpptxのままではClaude CodeなどのAIツールは読み取ってくれません。テキストコピペしても、図の情報が抜け落ちてしまいます。表のレイアウトもコピペでは崩れてしまい、AIが理解しづらくなります。
直接テキスト抽出では不十分な理由:
- 表の罫線情報が失われ、データの関係性がわからなくなる
- グラフ・図表の情報が抜けてしまう
- レイアウトによる暗黙の情報が伝わらない
そこで、画像ファイルを介し、LLMに認識させ、テキスト化する方法を試しました。
画像経由のメリット:
- 人間が見るのと同じ視覚情報をAIに渡せる
- 表の構造やグラフの傾向も読み取れる
- フォントサイズや配置による重要度も伝わる
全体の流れ
以下の3つのステップでPowerPointファイルをAI可読なMarkdownテキストに変換します。
- PowerPointファイルを、LibreOfficeでPDFファイルに変換
- PDFファイルを、Pythonのpdf2imageというライブラリでページごとに画像ファイルに変換
- 画像ファイルからLLMをOCR的にを利用してMarkdownテキストに変換
各ステップの詳細は後述します。
実際に変換してみる
このやり方で、実際に変換してみました。
サンプルとして以下のページで公開されているPowerPointファイルを利用します。
ダウンロードしたzipに含まれる 01_Overview.pptx の8ページ目と9ページ目の2スライド分を変換してみます。
変換結果
以下はPNG画像に変換したものです。なぜか1枚目のスライドはフォントがおかしくなっていますが、内容の理解には支障がないレベルです。
ちなみにフォントがおかしくなっているのはPNG化の段階ではなく、PDFの段階からでした。
テキストへの変換結果は、以下のようになりました。
# 自己回帰言語モデル (Autoregressive Language Models)
## 条件分布による表現
- p(x₁, x₂, ⋯, xₗ)を条件分布の積として表現する
```
p(x₁, x₂, ⋯, xₗ) = p(x₁)p(x₂|x₁) ⋯ p(xₗ|x₁, x₂, ⋯, xₗ₋₁)
```
## 自己回帰言語モデルの特徴
- このように確率の連鎖律で分解したモデルを特に自己回帰言語モデルと呼ぶ
- 条件付き確率がわかると、**生成することもできる**
## 確率の例
- p(東京 |日本, の, 首都, は) = **0.2**
- p(パリ |日本, の, 首都, は) = 0.001
- p(カイロ |日本, の, 首都, は) = 0.0005
## 文脈からの推論
この条件付き確率をどう求めるか?
右側の文脈から推論: 日本の首都は → **東京**
---
*LLM大規模言語モデル講座 議論資料 © 2024 by 東京大学松尾・岩澤研究室*
# ニューラル言語モデル
• 条件付き確率を何らかのニューラルネットで推定したモデル
• 他の学習と同様尤度を最大化するように訓練(誤差逆伝播)
## 図表説明
画像下部には、ニューラル言語モデルの動作例を示す図が描かれている:
- 4つの縦長の紫色の枠があり、それぞれが神経網のユニット(ニューロン)を表している
- 各枠内には4つの青い円が縦に並んでいる
- 枠の下部には順番に「日本」「の」「首都」「は」という日本語の単語が配置されている
- 4つの枠は左から右へ矢印で結ばれ、情報の流れを示している
- 最後の枠の上部から上向きの矢印が出ており、オレンジ色の棒グラフに向かっている
- 棒グラフの上部には「京都」と「東京」というラベルが付いており、「東京」の棒が最も高くなっている
この図は「日本の首都は」という文脈から次の単語(「東京」)を予測するニューラル言語モデルの動作を視覚化している。
---
*LLM大規模言語モデル講座 講義資料 © 2024 by 東京大学松尾・岩澤研究室*
フッター部のコピーライトも含めて、しっかり内容が説明されています。
各ツールの使い方
1. LibreOffice: オフィスファイルをPDFに変換
以下のようにして、LibreOfficeを使える状態にします。
apt install -y libreoffice fonts-noto-cjk fonts-noto-cjk-extra locales
LibreOfficeをヘッドレスモードで呼び出します。今回はPerlスクリプトで書きました。
コードの要点だけ書いておきます。
use File::Basename;
use File::Temp qw(tempdir);
use IPC::Run3;
sub convert_office_to_pdf {
my ($office_file_path) = @_;
my $temp_output_dir = dirname($office_file_path);
my $temp_user_dir = tempdir(CLEANUP => 1);
# Set up environment
local %ENV = %ENV;
$ENV{HOME} = $temp_user_dir;
$ENV{TMPDIR} = $temp_user_dir;
$ENV{LANG} = 'ja_JP.UTF-8';
$ENV{LC_ALL} = 'ja_JP.UTF-8';
$ENV{JAVA_TOOL_OPTIONS} = '-Djava.awt.headless=true';
my @cmd = (
'libreoffice',
'--headless',
'--invisible',
'--nodefault',
'--nolockcheck',
'--nologo',
'--norestore',
'--convert-to', 'pdf',
'--outdir', $temp_output_dir,
"-env:UserInstallation=file://$temp_user_dir/.config/libreoffice",
$office_file_path,
);
my ($stdout, $stderr);
eval {
local $SIG{ALRM} = sub { die "timeout\n" };
alarm 60; # 60 second timeout
run3(\@cmd, \undef, \$stdout, \$stderr);
my $exit_code = $? >> 8;
alarm 0;
print $stdout if $stdout;
print STDERR $stderr if $stderr;
if ($exit_code == 0) {
my $base_name = basename($office_file_path);
$base_name =~ s/\.[^.]*$//; # Remove extension
my $temp_pdf_path = "$temp_output_dir/$base_name.pdf";
unless (-f $temp_pdf_path) {
print "Error: PDF file not created at $temp_pdf_path\n";
return;
}
return $temp_pdf_path;
} else {
print "LibreOffice conversion failed: $stderr\n" if $stderr;
return;
}
};
}
report.pptx に対して report.pdf という名前のPDFファイルが生成されます。
対応形式:
- Word:
.doc
,.docx
- Excel:
.xls
,.xlsx
- PowerPoint:
.ppt
,.pptx
なお、LibreOfficeを使っているのでWord、Excelも同様に変換できます。ただし、Excelは大きな表だとPDFにしたときに読み取りづらくなるかもしれません。
2.Pythonのpdf2image: PDFを画像に分解
PDFの各ページをPNG画像ファイルにします。
pip install pdf2image
Pythonのコードの要点だけを書いておきます。
import pdf2image
def convert_pdf_to_images(pdf_path):
images = pdf2image.convert_from_path(pdf_path, dpi=400, fmt='PNG')
total_pages = len(images)
page_width = len(str(total_pages))
dir_path = f"{pdf_path}.pages"
os.makedirs(dir_path, exist_ok=True)
for idx, image in enumerate(images):
image_file_path = f"{dir_path}/{idx + 1:0{page_width}d}.png"
print(f"Saving page {idx + 1} of {total_pages} as PNG...: {image_file_path}")
image.save(image_file_path, 'PNG')
report.pdf に対して report.pdf.pages/ というディレクトリが作成され、各ページが 001.png
, 002.png
のように保存されます。
report.pdf.pages/
├── 001.png # Page 1
├── 002.png # Page 2
├── 003.png # Page 3
└── ...
3. LLMで画像をMarkdownテキストに変換
最後の画像をテキストにするところはLLM頼みです。今回はClaude Codeを使いました。
Claude Codeがインストールされていることを前提で、Claude Codeを呼び出し、結果をMarkdownファイルに書き下すラッパースクリプトを以下のようにPerlスクリプトを書きました。
コードの要点だけ書いておきます。
use File::Spec;
sub convert_image_to_text {
my ($input_file) = @_;
my $prompt =
"この画像の内容をAI可読なテキストに変換してください。" .
"画像にテキストが含まれている場合は正確にOCRで読み取り、" .
"図表やグラフィック要素がある場合はその内容を詳細に説明してください。" .
"出力は構造化されたMarkdown形式で、読みやすく整理してください。\n" .
"対象の画像: $input_file\n\n" .
"結果のみをMarkdownテキストで出力し、他の補足説明等は不要です\n";
print STDERR "Using prompt: $prompt\n";
my $abs_input_file = File::Spec->rel2abs($input_file);
my @claude_cmd = (
"claude",
"-p", $prompt,
);
my $input_dir = dirname($abs_input_file);
my $old_cwd = File::Spec->rel2abs(".");
chdir($input_dir);
open(my $claude_fh, "-|", @claude_cmd) or die "Failed to execute Claude Code: $!";
my $output = do { local $/; <$claude_fh> };
close($claude_fh);
# Restore original directory
chdir($old_cwd);
if ($? != 0) {
print STDERR "Error: Claude Code execution failed with exit code $?\n";
print STDERR "Command: " . join(" ", @claude_cmd) . "\n";
return "";
}
return $output;
}
良い点
- テキスト資料はそのまま起こせる
- 表はMarkdown表形式に変換される
- 図はテキストで詳細に説明される
- レイアウト情報も補足される
制約
Claude Codeを使って1ページずつ処理しているので、時間とお金がかかります。
使い道
PowerPointをテキスト化しておくと、Claude CodeやCursorといったAI開発環境で直接参照でき、RAGにも取り込みやすくなります。
AIが資料を理解できる形式に整えることで、既存資産の活用範囲が広がります。