DOCX 轉 Markdown 轉換器:粗體設定是一場協商,而非定論
My DOCX converter learned that 'bold' is a negotiation, not a fact
作者改進 DOCX 轉 Markdown 轉換器,不再只依賴 run.bold 判斷粗體,而是按直接格式、字元樣式、段落樣式和文件預設樣式等逐層檢查。轉換器亦會根據大綱層級辨識標題,並直接讀取 XML 處理合併儲存格。作者將轉換功能封裝成單一 API 呼叫,可接收 DOCX URL 或 base64 bytes,輸出保留標題、清單和表格的 Markdown。
I built a DOCX-to-Markdown converter thinking Word documents were simpler than PDFs. They're not. They're just lying in a different font.
The first version used python-docx and trusted run.bold. Then I fed it a contract drafted by a law firm. The entire document rendered as plain text. Turned out that document used character styles named "Strong" and "Emphasis" — visually bold and italic, but run.bold was None everywhere. Word doesn't have a single source of truth for formatting. It has at least four, and they fight.
So I added a fallback chain: direct run properties → character style → paragraph style → the document's default styles → whatever LibreOffice would have done. I also had to handle w:b vs w:bCs (complex script bold, because of course there's a separate one), and w:i for italics which some templates set to false explicitly to override an inherited italic.
The real gotcha: a "Heading 1" style that isn't actually a heading. Designers restyle it to look like body text. My converter now checks the outline level (w:outlineLvl in the paragraph properties), not the style name. If outline level 0 is set, it's a heading no matter what the font size says.
Tables were the second war. DOCX tables can have nested tables, merged cells that span rows and columns, and cells with no text but a single empty paragraph. python-docx gives you the grid; it does not give you "what a human sees." I ended up walking the XML directly for merges (w:vMerge and w:hMerge with continue vs restart) because the library's cell.text lies about merged regions.
The lesson: DOCX is a presentation format wearing a document format's clothes. If you're converting it for an LLM, don't trust the style names. Trust the outline levels, the direct formatting, and the raw XML hierarchy. Word users will absolutely use a "Normal" style paragraph with manual bold at 28pt as their title, and your converter has to survive that.
I packaged the whole thing as a single API call — send a DOCX URL or base64 bytes, get clean Markdown back with headings, lists, and tables preserved: https://x402.freeq.one/tools/docx_to_markdown.html
Anyone else fighting Word's style soup? What's your worst DOCX horror story?
來源:DEV Community · dev.to