[Python] マークダウン形式ファイルからWord文書作成する

スポンサーリンク

備忘録です。

Word文書をマークダウン形式ファイルに変換するスクリプトを作成したのでメモです。

 

import os
import re
from docx import Document
from docx.enum.text import WD_COLOR_INDEX
from docx.shared import RGBColor

def add_formatted_text(paragraph, text):
    """
    1つの段落内で太字(**)、インラインコード(`)、隅付き括弧(【】)、
    および図表番号(図 3-1:タイトル等、スペースありも含む)を解析して、
    適切な書式を適用しながら追加するよ。
    """
    # 解析するパターンの定義
    # 1. 太字: \*\*.*?\*\*
    # 2. コード: `.*?`
    # 3. 隅付き括弧: 【.*?】
    # 4. 図表番号: (?:図|表)\s?\d+-\d+(?::[^\s]+)?  (図か表で始まり、任意のスペース、数字-数字、あればタイトル)
    pattern = r'(\*\*.*?\*\*|`.*?`|【.*?】|(?:図|表)\s?\d+-\d+(?::[^\s]+)?)'
    parts = re.split(pattern, text)
    
    for part in parts:
        if not part:
            continue
            
        # 太字(マークダウン形式)
        if part.startswith('**') and part.endswith('**'):
            content = part[2:-2]
            run = paragraph.add_run(content)
            run.bold = True
            
        # 隅付き括弧
        elif part.startswith('【') and part.endswith('】'):
            run = paragraph.add_run(part)
            run.bold = True
            
        # 図番号・表番号(スペースを削除して太字にするよ)
        elif re.match(r'^(図|表)\s?\d+-\d+', part):
            # 含まれているスペースを削除して「図3-1」の形に整えるよ
            clean_part = part.replace(' ', '')
            run = paragraph.add_run(clean_part)
            run.bold = True
            
        # インラインコード(黒背景 + 白文字 + 等幅フォント)
        elif part.startswith('`') and part.endswith('`'):
            content = part[1:-1]
            run = paragraph.add_run(content)
            run.font.highlight_color = WD_COLOR_INDEX.BLACK
            run.font.color.rgb = RGBColor(255, 255, 255)
            run.font.name = 'Courier New'
            
        # 普通のテキスト
        else:
            paragraph.add_run(part)

def convert_md_to_docx(md_filepath, template_path, output_path):
    """
    マークダウンを読み込んで、表やリスト、装飾に対応してWordに流し込むよ。
    """
    if os.path.exists(template_path):
        doc = Document(template_path)
    else:
        print("警告: テンプレートが見つからないから、新規作成するね。")
        doc = Document()

    with open(md_filepath, 'r', encoding='utf-8') as f:
        lines = f.readlines()

    table_data = []
    in_table = False
    list_counter = 1

    for line in lines:
        clean_line = line.strip()

        # --- 表(テーブル)の処理 ---
        if '|' in clean_line:
            if re.match(r'^[|\s:-]+$', clean_line):
                continue
            cells = 
            if cells:
                table_data.append(cells)
                in_table = True
            continue
        else:
            if in_table and table_data:
                create_word_table(doc, table_data)
                table_data = []
                in_table = False

        # --- 空行の処理 ---
        if not clean_line:
            list_counter = 1
            doc.add_paragraph() 
            continue

        # --- スタイルの適用と装飾処理 ---
        if clean_line.startswith('### '):
            p = doc.add_paragraph(style='Heading 3')
            add_formatted_text(p, clean_line[4:])
            list_counter = 1
        elif clean_line.startswith('## '):
            p = doc.add_paragraph(style='Heading 2')
            add_formatted_text(p, clean_line[3:])
            list_counter = 1
        elif clean_line.startswith('# '):
            p = doc.add_paragraph(style='Heading 1')
            add_formatted_text(p, clean_line[2:])
            list_counter = 1

        # --- リストの処理(テキスト形式) ---
        elif clean_line.startswith('- ') or clean_line.startswith('* '):
            p = doc.add_paragraph("・")
            add_formatted_text(p, clean_line[2:])
            list_counter = 1
            
        elif re.match(r'^\d+\. ', clean_line):
            text = re.sub(r'^\d+\. ', '', clean_line)
            p = doc.add_paragraph(f"{list_counter}. ")
            add_formatted_text(p, text)
            list_counter += 1

        # 普通の文章
        else:
            p = doc.add_paragraph()
            add_formatted_text(p, clean_line)
            list_counter = 1

    if in_table and table_data:
        create_word_table(doc, table_data)

    doc.save(output_path)
    print(f"作成完了!: {output_path}")

def create_word_table(doc, data):
    """
    貯めたデータからWordのテーブルを作成する補助関数。
    """
    rows = len(data)
    cols = len(data[0])
    table = doc.add_table(rows=rows, cols=cols)
    
    try:
        table.style = 'Table Grid'
    except KeyError:
        print("ヒント: 'Table Grid' が見つからないよ。")

    for r in range(rows):
        for c in range(cols):
            if c < len(data[r]):
                cell_p = table.rows[r].cells.paragraphs[0]
                add_formatted_text(cell_p, data[r])

if __name__ == "__main__":
    # --- ここを自分の環境に合わせて書き換えてね ---
    filename = "for"

    # 読み込むマークダウンファイル
    input_md = f"./{filename}.md"
    # 用意したテンプレートファイル
    template_file = "afterschool.docx"
    # 出力するファイル名
    output_docx = f"./{filename}.docx"
    
    # テスト用のマークダウンを作成
    if not os.path.exists(input_md):
        with open(input_md, "w", encoding="utf-8") as f:
            f.write("# 図表番号テスト\n\n")
            f.write("図 3-1:タイトル という風にスペースがあっても、Wordでは図3-1になるよ。\n\n")
            f.write("表 1-2 も同じようにスペースを消して太字にするね。\n\n")
            f.write("もちろん、図3-2:タイトル みたいに元からスペースがなくても大丈夫だよ。\n")

    try:
        convert_md_to_docx(input_md, template_file, output_docx)
    except Exception as e:
        print(f"エラーが発生したよ:{e}")
Please follow and like us:

コメント

タイトルとURLをコピーしました