DOCX 建立、編輯與分析
.docx 檔案是 XML 檔案的 ZIP 壓縮包。依任務選擇方法:
| 任務 | 方法 |
|---|---|
| 建立新文件 | 撰寫 docx (npm) 腳本 — 請注意下方陷阱 |
| 編輯現有文件 | unzip → 編輯 word/document.xml → zip (docx-js 無法開啟既有檔案) |
| 讀取內容 | pandoc -t markdown file.docx |
下方腳本路徑相對於此技能目錄。
使用 docx-js 建立文件 — 陷阱
docx 已預先安裝 — 請勿先執行 npm install;直接撰寫腳本並 require('docx')。僅在 require 失敗時才執行 npm install docx。模型已熟知 API;以下是常見陷阱:
- 頁面大小預設為 A4。 若要使用 US Letter,請設定
page: { size: { width: 12240, height: 15840 } }(單位 DXA;1440 = 1 英寸)。 - 橫向: 傳入縱向尺寸並加上
orientation: PageOrientation.LANDSCAPE— docx-js 會在內部交換寬高。 - 表格需要雙重寬度: 同時設定表格的
columnWidths以及每個儲存格的width,兩者皆使用WidthType.DXA(PERCENTAGE在 Google Docs 中會失效)。欄寬總和必須等於表格寬度。 - 表格底紋: 使用
ShadingType.CLEAR,永遠不要用SOLID(會渲染為黑色)。 - 清單: 絕對不要直接插入
•符號;應使用包含LevelFormat.BULLET的numbering設定。 ImageRun需要type:("png"、"jpg"…)。PageBreak必須放在Paragraph內部。- 永遠不要使用
\n— 應使用獨立的Paragraph元素。 - 目錄: 標題必須使用內建的
HeadingLevel.*;自訂標題樣式需要設定outlineLevel,否則不會顯示在目錄中。 - 不要用表格當作水平線 — 改用段落底部框線。
- 點狀前導/同一行靠右對齊: 在
TextRun內使用PositionalTab(alignment: PositionalTabAlignment.RIGHT、leader: PositionalTabLeader.DOT),不要手動輸入.或空白填充。
驗證輸出
寫好 .docx 後,將其轉換並檢視結果:
python scripts/office/soffice.py --headless --convert-to pdf output.docx
pdftoppm -jpeg -r 100 output.pdf page
ls page-*.jpg # 然後讀取這些圖片
pdftoppm 會根據總頁數補零(page-01.jpg…page-12.jpg)。
編輯現有文件
舊版 .doc 檔案必須先轉換:python scripts/office/soffice.py --headless --convert-to docx file.doc。
unzip -q doc.docx -d unpacked/
find unpacked -type l -delete # 移除符號連結項目 — 來自信任外的 docx 可能不安全
python scripts/merge_runs.py unpacked/ # 合併分散的文字區塊,使文字可被搜尋
# 直接在 unpacked/word/document.xml 中編輯 — 不要重新格式化或美化解
(cd unpacked && rm -f ../out.docx && zip -Xr ../out.docx .)
python scripts/office/validate.py out.docx --original doc.docx # XSD 檢查;--auto-repair 可修復常見問題
# 若有修訂追蹤?加上 --author "<你的修訂名稱>" 以確認每次編輯都有被追蹤
Word 會將文字分散在多個 <w:r> 區塊中(因修訂編號、拼字檢查標記等),因此你在文件中看到的詞句,在 XML 中通常不是一個連續字串。merge_runs.py 會合併 word/document.xml 中相鄰且格式相同的區塊,不改變內容或顯示;它也可以直接接受 .docx 檔案(python scripts/merge_runs.py doc.docx -o merged.docx)。
修訂追蹤: 進行修訂時,請使用 --author "<你的修訂名稱>"(需搭配 --original)進行驗證 — 它會回報任何你修改但未包在 <w:ins> / <w:del> 中的文字,這類情況很容易不小心發生,且在已接受的檢視中不可見。將區塊包在 <w:ins> / <w:del> 中,並附上 w:id、w:author、w:date 屬性。在 <w:del> 內部,文字元素是 <w:delText>,而非 <w:t>。刪除的段落標記(<w:pPr><w:rPr><w:del w:id=".." w:author=".." w:date=".."/></w:rPr></w:pPr>)表示「將此段落合併到下一段」— 因此完全刪除一個段落就是上述標記加上包住每個文字區塊的 <w:del>。<w:del/> 必須出現在 rPr 的其他子元素之前;它們的順序是由結構定義(schema)強制規定的。
若要產生一份接受所有修訂的乾淨副本:python scripts/accept_changes.py in.docx out.docx。
接受一個刪除的段落標記應將該段落與其後的段落合併,因此所有文字區塊都被刪除的段落會消失。Word 會這麼做;但 accept_changes.py 和 pandoc --track-changes=accept 不一定能正確處理。兩者失敗的方式相同 — 它們會移除被刪除的文字,但留下空的段落。當該段落是自動編號時,看起來就像一個多餘的空項目符號:
pandoc --track-changes=accept永遠不會合併段落。accept_changes.py(LibreOffice)能正確合併,但當刪除的段落後面緊接著一個空白的間隔段落時例外。
無論哪種檢視中出現的空項目符號,都是檢視方式造成的假象,並非文件本身的缺陷。請在 XML 中檢查段落刪除。
註解
註解需要六個彼此關聯的檔案。使用輔助腳本 — 如果你同時也要編輯 document.xml,請使用目錄模式(可省去解壓縮/重新封裝的步驟);否則使用 .docx 直接模式:
# 針對已解壓縮的目錄(同時放置標記時較佳)
python scripts/comment.py unpacked/ "費用上限過低"
python scripts/comment.py unpacked/ "同意" --parent 0
# 直接針對 .docx 檔案
python scripts/comment.py contract.docx "此上限過低" -o annotated.docx
腳本會寫入 comments.xml、commentsExtended.xml、commentsIds.xml、commentsExtensible.xml、關係檔案以及內容類型覆寫。註解 ID 會自動指定。接著腳本會輸出需加入 word/document.xml 的 <w:commentRangeStart> / <w:commentRangeEnd> / <w:commentReference> 程式碼片段,以便將註解錨定到特定文字上 — 在你放置這些標記之前,註解雖然存在但不會顯示。
依賴項目
docx (npm,已預先安裝 — 僅在 require('docx') 失敗時才安裝) · pandoc · LibreOffice (soffice) · pdftoppm(Poppler)