PyMuPDF4LLMは、PDFを見出し・表・箇条書きの構造を残したMarkdownに変換するPythonライブラリです。pymupdf4llm.to_markdown("input.pdf")の1行で動き、GPUも要りません。この記事では2026年10月時点の最新版1.28.2で全コードを実行し直し、公式ドキュメントに書かれていない日本語PDFでの癖、表抽出のモードによる差、1.28.2で入ったコマンドラインでの一括変換まで、実際の出力を示しながら説明します。
まとめ
- 導入は
pip install pymupdf4llm。Python 3.10以上が必要で、PyMuPDFとレイアウト解析のpymupdf_layoutが同じ版番号で入る。 - 変換は
to_markdown()。プレーンテキストならto_text()、座標付きの構造データならto_json()を使う。 - 日本語のスキャンPDFは
ocr_language="jpn"を必ず付ける。既定の"eng"のままだと文字化けした結果が返る。 - 罫線の無い表を崩さずにMarkdown表にできたのは既定のレイアウトモードだけ。
table_strategyやmarginsはuse_layout(False)の従来モードでしか効かない。 - 1.28.2から
pymupdf4llmコマンドでフォルダ内のPDFを一括変換できる。日本語OCRは--opt ocr_language=jpnで指定する。 - ライセンスはAGPL-3.0とArtifex商用ライセンスのデュアル。ソースを公開しない製品やSaaSに組み込むなら商用ライセンスが要る。
PyMuPDF4LLMとPyMuPDF・pymupdf_layoutの関係
PyMuPDF4LLMは、PDF処理エンジンのPyMuPDFの上に載った変換用のパッケージです。PyMuPDF単体のpage.get_text()は見出しや表の区別がない平文を返しますが、PyMuPDF4LLMはページを解析して見出しに#、表にパイプ記法を付けたMarkdownを組み立てます。PDFを開く・編集する・画像を抜くといった操作そのものはPyMuPDFの役割で、本体の使い方はPyMuPDF(fitz)とは|インストールから使い方・ライセンスまで解説【2026年版】で扱っています。
もう1つの構成要素がpymupdf_layoutです。PDFの内部情報(文字の位置・フォント・罫線)から「これは見出し」「これは表」「これはページヘッダー」と領域を分類するモデルで、ONNX Runtime上のCPUだけで動きます。変更履歴によると1.27.2.1から自動でインストールされ、import pymupdf4llmの時点で有効になります。以前の解説にある「import pymupdf.layoutを先に書く」手順は、現行版では要りません。止めたいときはpymupdf4llm.use_layout(False)を呼ぶと、pymupdf_layoutを使わない従来の処理(以下、従来モード)に切り替わります。
PyMuPDF4LLMのインストールと動作要件
pip install pymupdf4llm
PyPIの1.28.2(2026年8月6日公開)は、Python 3.10以上を要求し、pymupdf==1.28.2・pymupdf_layout==1.28.2・tabulate・psutilに依存します。3つのパッケージは版番号を揃えて固定されているので、PyMuPDFだけを別の版に上げると依存の解決で衝突します。
インストール容量は軽くありません。Python 3.12の仮想環境に1.28.2を入れると、site-packagesは252MBになりました。内訳はPyMuPDFが110MB、pymupdf_layoutが引き込むonnxruntimeが69MB、sympyが29MB、numpyが27MBです。この容量は検証したmacOS・Python 3.12環境の参考値です。コンテナの容量は、対象OS・CPU向けの依存パッケージとベースイメージを含めて別途測定してください。
スキャンページをOCRするには、TesseractかRapidOCR(rapidocr_onnxruntime)のどちらかが実行環境に必要です。PyMuPDF4LLMは見つかった方を自動で使います。Tesseractの導入と日本語データの入れ方はTesseract OCRの使い方|インストールと日本語設定・主要コマンド【5.5.3対応】にまとめています。
to_markdownによるPDFのMarkdown変換
to_markdownの最小コードとUTF-8保存
from pathlib import Path
import pymupdf4llm
md_text = pymupdf4llm.to_markdown("input.pdf")
Path("output.md").write_bytes(md_text.encode())
戻り値は全ページを結合した1本の文字列です。write_bytes(md_text.encode())はUTF-8で書き出すので、Windowsでopen()の既定エンコーディングがcp932になって日本語が化ける事故を避けられます。第1引数にはファイルパスのほか、pymupdf.open()で開いたDocumentも渡せます。パスワード付きPDFはdoc.authenticate("パスワード")で開いてから渡すと変換できました。
to_markdownの主要引数とモード別の対応範囲
公式のAPIリファレンスには30以上の引数がありますが、レイアウトモードで実際に効くのは次の範囲です。
| 引数 | 既定値 | 用途 |
|---|---|---|
| pages | None(全ページ) | 0始まりのページ番号リスト |
| header / footer | True | Falseで各ページのヘッダー・フッターを除外 |
| page_chunks | False | Trueでページごとの辞書リストを返す |
| write_images | False | 図をPNGで書き出しMarkdownから参照 |
| embed_images | False | 図をbase64で本文に埋め込む |
| use_ocr | True | 文字の無いページを自動でOCR |
| force_ocr | False | 全ページを強制的にOCR |
| ocr_language | “eng” | TesseractのOCR言語 |
| show_progress | False | 進捗バーを表示 |
table_strategy・margins・fontsize_limit・ignore_imagesなどは、リファレンスに「use_layout() must be False」と書かれた従来モード専用の引数です。レイアウトモードでtable_strategy="lines"を渡しても表の検出は変わりません。表が崩れたときに最初に疑うのはこの引数ではなく、次の章で扱うモードの違いです。
force_ocr=Trueは、文字が入っているのに化けているPDFに限って使います。公式ドキュメントも、きれいなテキストPDFに強制OCRをかけると処理が大幅に遅くなり、品質も落ちうると注意しています。
to_textとto_jsonの使い分け
同じ解析結果を別の形式で出す関数が2つあります。to_text()は記号を付けないプレーンテキストで、Markdownの#や|がノイズになる全文検索の索引に向きます。to_json()はページごとの領域(boxclassがpage-header・section-header・textなど)と座標、フォント名・サイズまで含むJSON文字列を返します。帳票から特定の位置の値を抜く、見出しの階層を自前で判定するといった、Markdownでは情報が足りない処理に使います。
日本語PDFで出る癖と直し方
公式ドキュメントのサンプルは英語のPDFだけです。そこで、日本語の見出し・本文・表・ヘッダー・フッターを持つ2ページと、文字を画像にしたスキャン相当の1ページからなるPDFを作り、1.28.2で変換しました。検証環境はmacOS、Python 3.12、Tesseract 5.5.3(eng・jpn)です。出てきた癖は3つです。
日本語スキャンPDFのOCR言語設定
文字を選択できないページは自動でOCRされますが、OCR言語の既定値は"eng"です。日本語のスキャンページを既定のまま変換すると、「第4章 附則」のページが# 45 AYAという意味の無い文字列になりました。ocr_language="jpn"を付けると「第4章 附則」「この規程は2026年4月1日から施行する。」と正しく出ます。
import pymupdf4llm
md_text = pymupdf4llm.to_markdown(
"input.pdf",
header=False,
footer=False,
ocr_language="jpn",
)
Tesseract側に日本語の学習データ(jpn.traineddata)が入っていることが前提です。英語と日本語が混ざる文書は"jpn+eng"のように+でつなぎます。OCRがかかるのは文字の無いページと、文字化けが多い領域だけです。文字だけのページはOCRに回りません。
和文の空白除去と見出しの太字補正
テキストを持つページでも、日本語には次の2つの癖が出ました。空白の挿入は両モードで、見出し中の数字の太字化はレイアウトモードで確認しました。
- 和文と数字の間、PDF上で改行していた位置に半角空白が入る(「翌月 10 日」「電子帳簿保存法に 従います」)
- レイアウトモードでは、見出し中の数字だけが太字になる(「### 第 **1** 章 経費精算」)
LLMに読ませるだけなら大きな害はありませんが、ベクトル検索や全文検索では「10日」と「10 日」が別のトークン列になり、キーワード一致が外れます。索引に入れる前に、和文の前後の空白だけを詰める後処理を挟んでください。次の関数は英単語どうしの空白を残しますが、空白の由来までは判定しません。コードブロックや、空白で区切った和文の固有名詞を含む文書では、本文の段落だけに適用してください。
import re
CJK = r"-ヿ㐀-鿿!-⦆"
def normalize_ja(md: str) -> str:
# 見出しの数字だけに付いた太字を外す
md = re.sub(
r"(?m)^(#{1,6} +)([^\n]*)$",
lambda m: m[1] + re.sub(r"\*\*([0-9][0-9.\-]*)\*\*", r"\1", m[2]),
md,
)
# 和文と数字の間、改行由来で和文の途中に入った空白を詰める
md = re.sub(rf"(?<=[{CJK}]) +(?=[{CJK}0-9])", "", md)
md = re.sub(rf"(?<=[0-9]) +(?=[{CJK}])", "", md)
return md
この関数で「翌月10日までに申請します。領収書の電子保存は電子帳簿保存法に従います。」「### 第1章経費精算」に戻ります。見出しの「章」と章名の間の空白も詰まるので、見出しの体裁を保ちたい場合は見出し行を除外してください。チャンクの長さを日本語でどう決めるかはチャンクとトークンの違いとは?チャンク分割の手法とRAGのサイズ設計を日本語で実測【2026年版】で検証しています。
表の抽出結果:レイアウトモードと従来モードの差
同じページに罫線付きの表と罫線の無い表(3列×4行)を置いたPDFで、2つのモードを比べました。
| 比較項目 | レイアウトモード(既定) | 従来モード |
|---|---|---|
| 罫線付きの表 | Markdown表になる | 表になるが見出しを1行目に巻き込む |
| 罫線の無い表 | Markdown表になる | 既定では平文・text戦略で崩れた表 |
| OCR関連の引数 | 有効 | 無視される(警告が出る) |
| 60ページの処理時間 | 約21秒 | 約7秒 |
従来モードの罫線付きの表は、直前の見出し「罫線付きの表」を表の1行目に取り込み、|罫線付きの表|Col2|Col3|という余計な行ができました。従来モードでもtable_strategy="text"を指定すると罫線の無い表が表として出力されますが、行ごとに空の行と<br>が挟まり、直前の見出しも「線なしの表」と1文字欠けて表に取り込まれました。処理時間は、テキストだけの60ページのPDFを別プロセスで各2回変換した値(Intel Core i9-9880H・OCRなし・モデルの読み込みを含む)です。今回のテキストのみの60ページPDFでは、レイアウトモードの処理時間が従来モードの約3倍でした。
結論として、表を含む文書は既定のレイアウトモードのまま使ってください。従来モードに切り替えてよいのは、表が無く、数万ページを短時間で流す必要があり、OCRも要らない場合に限ります。ただしレイアウトモードでも検出漏れはあります。HTMLから生成したPDFの表は、同じ3列×4行でも表と判定されず、セルの値が1行に並んだ平文になりました。表の値をそのまま回答に使うRAGでは、変換後のMarkdownで表の部分だけ目で確かめる工程を外さないでください。
import pymupdf4llm
pymupdf4llm.use_layout(False) # 従来モードに切り替える(プロセス全体に効く)
md_text = pymupdf4llm.to_markdown("input.pdf", table_strategy="lines")
CLIによるフォルダ内PDFの一括変換(1.28.2以降)
1.28.2のパッケージにはpymupdf4llmコマンドが入りました(1.28.0のwheelには入っていません)。フォルダを渡すと、PDFごとにサブフォルダを作ってMarkdownと処理ログ(run-log.txt)を書き出し、CPUコア数とファイル数からワーカー数を自動で決めて並列処理します。
pymupdf4llm ./pdfs --out ./md --opt ocr_language=jpn
注意点が2つあります。1つ目は、CLIではヘッダーとフッターが既定で除外されることです。Pythonのto_markdown()は既定で含めるので、両者で出力が変わります。残したいときは--headerと--footerを付けます。
2つ目は、1.28.2の--ocr-lang jpnが変換処理に渡らないことです。ソースを読むと、__main__.pyが値をoptions["ocr_lang"]に入れていますが、変換関数が受け取る引数名はocr_languageで、この値はどこからも参照されていません。実際に--ocr-lang jpnで日本語のスキャンページを変換すると英語OCRの化けた結果になり、上のように--opt ocr_language=jpnで渡すと正しく出ました。出力形式は--backend md|json|txt、対象ファイルは--pattern(既定*.pdf)で変えられます。
RAG向けのpage_chunks・画像処理とLlamaIndex・LangChain連携
page_boxesによる出典付きチャンクの生成
page_chunks=Trueにすると、戻り値がページごとの辞書のリストになります。1.28.2のレイアウトモードで返るキーはmetadata・toc_items・page_boxes・textの4つでした。古い解説にあるtables・images・wordsは従来モードでしか返りません。
page_boxesは領域ごとに種類(class)と、text内の位置(pos)を持ちます。これを使うと、ヘッダー・フッターを落としながら領域単位でチャンクを作り、ファイル名とページ番号を出典として付けられます。
import pymupdf4llm
chunks = pymupdf4llm.to_markdown("input.pdf", page_chunks=True, ocr_language="jpn")
records = []
for ch in chunks:
meta = ch["metadata"]
for box in ch["page_boxes"]:
if box["class"] in ("page-header", "page-footer"):
continue
start, stop = box["pos"]
text = ch["text"][start:stop].strip()
if text:
records.append({
"source": meta["file_path"],
"page": meta["page_number"], # 1始まり
"class": box["class"], # section-header / text / list-item など
"text": text,
})
引数のpagesは0始まり、出力のpage_numberは1始まりです。取り違えると出典のページが1つずれます。section-headerの領域を区切りにして直後の本文とまとめれば、見出し単位のチャンクになります。取り込みと検索をどう分けて設計するかはRAGパイプラインとは?取り込み系と推論系に分ける工程設計と再索引の実装判断を参照してください。
write_imagesによる図の画像保存
md_text = pymupdf4llm.to_markdown(
"input.pdf",
write_images=True,
image_path="images",
dpi=200,
)
図の領域がPNGで保存され、Markdownにはのような参照が入ります。図の領域にある抽出可能な文字は、既定のforce_text=Trueで本文にも出力されます。ただし、画像の画素にしか存在しない文字の抽出にはOCRが必要です。force_text=Falseにすると、図の領域の文字は本文から外れて画像の中にだけ残るので、RAGに使う場合は既定値のままにしてください。embed_images=Trueはbase64で埋め込むためMarkdownが大きく膨らみ、チャンクに画像データが混ざるのでRAGには向きません。
LlamaIndex・LangChainのローダー連携
import pymupdf4llm
reader = pymupdf4llm.LlamaMarkdownReader()
docs = reader.load_data("input.pdf")
LlamaIndexのLlamaMarkdownReaderは、llama-index本体を別途インストールしないとNotImplementedErrorになります。LangChainでは公式パッケージlangchain-pymupdf4llmのPyMuPDF4LLMLoaderを使います。
from langchain_pymupdf4llm import PyMuPDF4LLMLoader
loader = PyMuPDF4LLMLoader(file_path="input.pdf", mode="page")
docs = loader.load()
langchain-pymupdf4llmの最新版1.28.0は依存をpymupdf4llm==1.28.0に固定しています。1.28.2を入れた環境に追加すると1.28.0へ戻り、上で紹介したCLIが使えなくなります。CLIを使うなら、LangChainのローダーとは仮想環境を分けてください。
PDFをMarkdownに変換する他のツールとの選び分け
PDFからMarkdownを作る手段はPyMuPDF4LLMだけではありません。ライセンスと得意な文書で選びます。
| ツール | 最新版 | ライセンス | 向く文書 |
|---|---|---|---|
| PyMuPDF4LLM | 1.28.2 | AGPL-3.0/商用 | テキストPDFの大量変換 |
| Marker | 2.0.0 | Apache-2.0+重みは条件付き | 数式・複雑な段組み |
| Docling | 2.137.0 | コードはMIT/モデルは個別条件 | コードの公開義務を避けたい場合 |
| MinerU | 4.0.12 | 独自ライセンス | 数式や表の多い論文 |
| MarkItDown | 0.1.8 | MIT | Office文書を含む多形式 |
選び方を言い切ると、デジタル生成のPDFが中心で、GPUを使わずに大量に流したいならPyMuPDF4LLMです。ソースを公開しないSaaSに組み込むのに商用ライセンスを買う予定が無いなら、PyMuPDF4LLMは外し、コードがMITのDoclingを選びます。Markerは2.0.0(2026年7月20日)でコードがApache-2.0になりましたが、モデルの重みは別ライセンスです。前年の総売上または累計の株式・借入による調達額が500万米ドルを超える場合、個人利用・研究目的の例外を除き別途許諾が必要です。競合製品・サービスの提供にも別の制限があります。各ツールの導入手順と判断材料は、Marker(marker-pdf)の使い方|PDFをMarkdown変換する手順と商用ライセンス制約、Doclingとは?PDFをMarkdown化してRAGに載せる実装手順と採用判断、MinerUとは?PDFをMarkdownに変換するOSS文書解析ツールの使い方、MarkItDown(マークイットダウン)とは|各種ファイルをMarkdownに変換するMicrosoft製Pythonツールで個別に扱っています。
商用利用とAGPLライセンスの判断
PyMuPDF4LLM・PyMuPDF・pymupdf_layoutの3つは、いずれもGNU AGPL 3.0とArtifex商用ライセンスのデュアルライセンスです。AGPLは、改変したプログラムをネットワーク越しに利用させる場合にも、利用者へソースコードを提供する義務を課します。PDFの取り込みを含むWebサービスをAGPLで使うなら、そのサービスのソースを公開する前提になります。
社内のバッチでPDFをMarkdownに変換し、結果のテキストだけを使う構成では、プログラムを外部に配布もネットワーク提供もしないので、ソース公開の義務は生じにくくなります。境界が曖昧になるのは、変換処理を顧客向けの機能として提供するときです。その場合は採用前に商用ライセンスの見積もりを取り、取れないならDoclingに切り替えてください。社内文書を検索できるRAGの設計からPDFの取り込み方式の選定までを外部に任せたい場合は、RAG構築支援で相談を受け付けています。
よくある質問
PyMuPDFの読み方は?
「パイミューピーディーエフ」と読みます。MuPDFはArtifexが開発するC言語製のPDFエンジンで、そのPythonバインディングがPyMuPDFです。PyMuPDF4LLMは「PyMuPDF for LLM」を縮めた名前です。
pymupdf_layoutとは何ですか?外せますか?
PDF内部の文字や罫線の情報から見出し・表・ヘッダーなどの領域を分類する、CPUで動くレイアウト解析モデルです。1.27.2.1以降はPyMuPDF4LLMの必須依存なので、インストールからは外せません。処理で使わないようにするにはpymupdf4llm.use_layout(False)を呼びます。
PyMuPDFとPyMuPDF4LLMの違いは?
PyMuPDFはPDFを開く・描画する・編集する・テキストを抜くための土台のライブラリで、import pymupdf(旧名fitz)で使います。PyMuPDF4LLMはその上でMarkdown・JSON・テキストへの変換だけを担います。PDFを操作したいならPyMuPDF、LLMに渡す文書が欲しいならPyMuPDF4LLMです。
pip install pymupdf4llmで古い版が入るのはなぜですか?
Python 3.9の環境だと、pipはPython 3.9に対応する最後の版0.0.27(2025年7月)を入れます。0.0.27はpymupdf_layoutに依存しないため、この記事のレイアウトモードやpage_boxesは使えません。python -m pip show pymupdf4llmで版を確かめ、1.28.2でなければPython 3.10以上の環境を作り直してください。
PyMuPDF4LLMは商用利用できますか?
できます。条件は2通りで、AGPL-3.0に従ってソースを公開するか、Artifexから商用ライセンスを購入するかです。社内でPDFを変換して結果のテキストだけを使う用途なら、AGPLのままで運用できる場合が多いです。