docx2md.py 技術ドキュメント

プログラムの動作

docx2md.py は、Microsoft Word (.docx) ファイルの内容を抽出し、Markdown形式に変換するためのPythonスクリプトです。このプログラムは、Wordドキュメントが持つ複雑な要素、特に数式、画像、および多様なテキスト書式をMarkdownの記法で再現することを目的としています。

主な機能

  • テキストと書式の変換: Wordドキュメント内のプレーンテキストに加え、太字、斜体、上付き文字、下付き文字などの書式を対応するMarkdown記法(例: **太字**, *斜体*, ^上付き^, ~下付き~)に変換します。

  • 数式の変換: Wordのネイティブ数式(Office Math Markup Language, OMML)を解析し、LaTeX形式に変換します。これにより、インライン数式は $formula$、ディスプレイ(ブロック)数式は $$\nformula\n$$ の形式で出力され、MathJaxやPandocなどのレンダリングツールで表示可能になります。

  • 画像の抽出とリンク: Wordドキュメントに埋め込まれた画像を抽出し、指定されたディレクトリに保存します。出力されるMarkdownファイルには、これらの画像への相対パスを示すMarkdown画像リンク(例: ![alt_text](path/to/image.png))が挿入されます。

  • 構造化された要素の変換:

    • 見出し: Wordの見出しスタイル(「見出し 1」~「見出し 4」など)を、Markdownのハッシュ記号(#####)による見出しに変換します。

    • リスト: 番号付きリストと箇条書きリストを、Markdownのリスト記法に変換します。ネストされたリストにも対応しています。

    • テーブル: Wordのテーブルを、Markdownのテーブル構文に変換します。セル内のテキスト書式や数式も適切に処理されます。

解決する課題

本プログラムは、Wordドキュメントから情報を抽出し、以下のような用途で再利用可能にするための課題を解決します。

  • ドキュメントのポータビリティ向上: Wordに依存しないテキストベースの形式に変換することで、ドキュメントの共有や表示が容易になります。

  • ウェブコンテンツへの変換: Markdownは多くのウェブサイトやブログプラットフォームでサポートされており、本ツールを用いることでWordから直接ウェブコンテンツを生成する手助けとなります。

  • バージョン管理と共同編集: テキストベースのMarkdownは、Gitなどのバージョン管理システムと相性が良く、共同編集や変更履歴の追跡が容易になります。

  • 技術文書の作成: Wordで作成された技術文書に含まれる数式やコードスニペットを、より適切な形式でMarkdownに変換できます。

原理

docx2md.py は、python-docx ライブラリを使用してDOCXファイルを読み込み、その内部XML構造を解析することで、コンテンツの変換を行います。特に、Wordの数式 (OMML) の解析には lxml ライブラリを利用しています。

Wordドキュメントの構造解析

  1. DOCXファイルの読み込み: docx.Document(infile_path) を使用してDOCXファイルを開きます。

  2. 要素のイテレート: ドキュメントのbody要素から、Paragraph(段落)とTable(テーブル)の各ブロック要素を順に処理します。iter_inner_content() メソッドは、WordのXMLツリーの論理的な順序でこれらのブロック要素を提供します。

  3. 段落内の要素: 各Paragraph要素に対して、iter_runs_and_omath_in_order ジェネレータ関数が、段落内の Run オブジェクト(テキストの断片)とOMML数式要素を、XML内での出現順序に従って返します。これにより、テキストと数式が混在する行でも正しい順序で処理できます。

数式変換 (OMML to LaTeX)

Wordの数式は、OMML (Office Math Markup Language) と呼ばれるXML形式でDOCXファイル内に埋め込まれています。docx2md.py は、このOMMLを解析し、LaTeX形式に変換します。

  1. OMML要素の特定: 段落内で m:oMath (Office Math) または m:oMathPara (Office Math Paragraph) 要素を検出します。これらの要素がWordの数式を表します。

  2. XML解析: lxml ライブラリの etree を使用して、検出されたOMML要素のXMLツリーを解析します。

  3. 再帰的変換: parse_omml_element 関数がOMMLのXML要素を再帰的に走査し、対応するLaTeX構文に変換します。主な変換ルールは以下の通りです。

    • テキスト要素 (m:t):

      • 関数名(sin, cos, log など)は \sin, \cos, \log のようにLaTeXコマンドに変換されます。

      • ギリシャ文字、演算子、特殊記号などは SYMBOL_MAP 辞書(例: α\alpha, \int)を使用してLaTeXコマンドにマッピングされます。

    • 分数 (m:f): \frac{numerator}{denominator} に変換されます。

    • 上付き/下付き (m:sSup, m:sSub): base^{sup} または base_{sub} に変換されます。

    • 根号 (m:rad): \sqrt{expression} に変換されます。

    • 括弧/デリミタ (m:d): \left および \right コマンドを使用して、可変サイズの括弧を生成します。_latex_delimiter 関数が、Wordで使用されるユニコードの括弧文字をLaTeXで安全に扱えるトークン(例: {\{, \langle)に変換します。

    • 総和/積分 (m:nary): \sum_{sub}^{sup} base\int_{sub}^{sup} base のように変換されます。

  4. 出力形式: インライン数式は $LaTeX_formula$ で、ブロック数式は $$\nLaTeX_formula\n$$ で出力されます。ブロック数式は、その前後に必ず空行を挟みます。

テキスト書式の変換

process_runs_to_markdown 関数は、段落内のRunオブジェクトの書式属性(bold, italic, font.superscript, font.subscript)をチェックし、Markdownの対応する記法に変換します。同じ書式の連続するテキストは itertools.groupby を使用してグループ化され、結合されます。

  • 太字: **text**

  • 斜体: *text*

  • 太字と斜体: ***text***

  • 上付き文字: ^text^ (Pandoc記法)

  • 下付き文字: ~text~ (Pandoc記法)

画像抽出

  1. 画像要素の検出: run オブジェクト内に w:drawing 要素がある場合、画像が含まれていると判断します。

  2. リレーションシップIDの取得: w:drawing 要素内にある a:blip 要素の r:embed 属性から、画像データへのリレーションシップIDを取得します。

  3. 画像データの抽出と保存: doc.part.related_parts を使用して、リレーションシップIDに対応する画像データ(バイナリ)を取得します。このデータを指定された --imagedir ディレクトリにファイルとして保存します。

  4. Markdownリンクの生成: 保存された画像ファイルへの相対パスを含むMarkdown画像リンク ![ファイル名](ディレクトリ/ファイル名.png) を生成します。

見出し、リスト、テーブルの処理

  • 見出し: handle_paragraph 関数内で、段落のスタイル名(例: Heading 1)をチェックし、Markdownの # から #### までの見出し記号を付加します。

  • リスト:

    • Wordのリストは w:pPr (Paragraph Properties) 内の w:numPr (Numbering Properties) を参照して識別されます。

    • list_counters 辞書を使用して、各リストのIDとレベル (numId, ilvl) ごとにカウンターを保持し、正しい番号を生成します。

    • 箇条書きリストはアスタリスク (* ) を使用し、番号付きリストは番号とピリオド (1. ) を使用します。

    • インデントはリストのレベル (ilvl) に応じてスペース (    ) を付加することで再現されます。

  • テーブル: handle_table 関数が docx.table.Table オブジェクトを受け取り、Markdownのテーブル構文に変換します。

    • 最初の行はヘッダーとして扱われ、その下に区切り線 (|---|) が挿入されます。

    • 各セル内のテキストは get_formatted_cell_text 関数で書式を保持したまま処理され、改行は <br> タグに変換されます。

必要な非標準ライブラリとインストール方法

このプログラムを実行するには、以下の非標準Pythonライブラリが必要です。

  • python-docx: Microsoft Word (.docx) ファイルを読み書きするためのライブラリ。

  • lxml: XMLを高速に処理するためのライブラリ。WordのOMML数式を解析するために使用されます。

これらのライブラリは、Pythonのパッケージマネージャーである pip を使用してインストールできます。コマンドプロンプトやターミナルで次のコマンドを実行してください。

pip install python-docx lxml

必要な入力ファイル

  • ファイル形式: Microsoft Word ドキュメント (.docx)

  • データ構造: プログラムは標準的なDOCXファイル形式を想定しています。

    • 数式はWordのネイティブなOMML形式で埋め込まれている必要があります。画像として挿入された数式は変換されません。

    • 画像はWordドキュメントに埋め込まれた形式である必要があります。リンクされた画像は正しく処理されない可能性があります。

    • 見出し、リスト、テーブルはWordの標準機能で作成されていることを前提とします。カスタムスタイルが適用されている場合でも、スタイル名がWordの標準的な「Heading 1」や「List Paragraph」に類似していれば対応可能です。

生成される出力ファイル

  • Markdownファイル: コマンドライン引数 -o または --output で指定されたファイル名(例: output.md)でMarkdownファイルが生成されます。このファイルには、Wordドキュメントから変換されたテキスト、書式、数式、見出し、リスト、テーブルなどがMarkdown形式で含まれます。

  • 画像ファイル: コマンドライン引数 --imagedir で指定されたディレクトリ(デフォルトは images)に、Wordドキュメントから抽出された画像ファイルが保存されます。これらの画像ファイルは、出力されるMarkdownファイルから相対パスでリンクされます。

    • 画像ディレクトリが指定され、かつそのディレクトリ内に画像が一つも保存されなかった場合、空のディレクトリは削除されます。

コマンドラインでの使用例 (Usage)

docx2md.py はコマンドラインから実行され、引数を使用して入力ファイル、出力ファイル、およびその他のオプションを指定します。

python docx2md.py -i <入力Wordファイル名> -o <出力Markdownファイル名> [--imagedir <画像ディレクトリ名>] [--pause <一時停止フラグ>]

引数説明

  • -i, --input <入力Wordファイル名> (必須): 変換するWordドキュメント(.docx)のパスを指定します。例: report.docx

  • -o, --output <出力Markdownファイル名> (必須): 生成されるMarkdownファイルのパスを指定します。例: output.md

  • --imagedir <画像ディレクトリ名> (任意): 画像ファイルを保存するディレクトリ名を指定します。このパスは、出力Markdownファイルからの相対パスとして扱われます。指定がない場合、デフォルトで images というディレクトリが使用されます。例: media, attachments

  • --pause <一時停止フラグ> (任意): プログラムの処理完了後に、ユーザーがEnterキーを押すまでプログラムを一時停止させる場合に0以外の値を指定します。デフォルトは 0(一時停止なし)です。例: --pause 1

コマンドラインでの具体的な使用例

1. 基本的な変換

my_document.docx というWordファイルをMarkdown形式に変換し、my_output.md として出力します。画像がある場合、my_output.md と同じ階層に images ディレクトリが作成され、そこに保存されます。

python docx2md.py -i my_document.docx -o my_output.md

2. 画像ディレクトリを指定して変換

presentation.docxpresentation.md に変換し、抽出された画像を assets という名前のディレクトリに保存します。

python docx2md.py -i presentation.docx -o presentation.md --imagedir assets

実行結果の説明: presentation.md ファイルが生成され、presentation.md と同じディレクトリに assets ディレクトリが作成されます。assets ディレクトリ内には、Wordファイルから抽出された画像ファイル(例: image1.png, image2.jpg など)が保存されます。presentation.md 内では、これらの画像に対して ![alt_text](assets/image1.png) のようなMarkdownリンクが記述されます。

3. 処理完了後に一時停止する

report.docxreport.md に変換し、変換処理が完了した後に、ユーザーが手動でEnterキーを押すまでプログラムが終了しないようにします。これは、コマンドプロンプトやターミナルがすぐに閉じてしまうのを防ぎたい場合に便利です。

python docx2md.py -i report.docx -o report.md --pause 1

実行結果の説明: 変換が正常に完了した後、コンソールに以下のメッセージが表示され、ユーザーの入力を待ちます。

変換が正常に完了しました。出力ファイル: report.md
画像は images に保存されました。
処理が完了しました。Enterキーを押すと終了します...

ユーザーがEnterキーを押すと、プログラムが終了します。