关于 docx-processing-anthropic
面向需要创建、编辑或分析 DOCX 文件的用户,提供从文本提取到 XML 修改和文档验证的操作指南,并涵盖 docx-js 文档生成细节。
主要功能
- 01提取文档文本或原始 XML
- 02使用 docx-js 创建文档
- 03解包、修改并重新打包 DOCX
- 04处理修订、批注、图片和表格
- 05转换 PDF 与页面图像
- 06验证并修复文档结构
适用场景
- 01提取 DOCX 内容并保留修订
- 02创建并验证新的 DOCX 文件
- 03修改现有文档的 XML 内容
- 04将文档转换为 PDF 或图像
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lawve-ai/ awesome-legal-skills --skill docx-processing-anthropic安装后在 Claude Code 中输入/docx-processing-anthropic确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSE.txtscripts/__init__.pyaccept_changes.pycomment.pyscripts/office/pack.pyscripts/office/helpers/__init__.pymerge_runs.pysimplify_redlines.pyscripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsdopc-coreProperties.xsdopc-digSig.xsdopc-relationships.xsdscripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsddml-chartDrawing.xsddml-diagram.xsddml-lockedCanvas.xsddml-main.xsddml-picture.xsddml-spreadsheetDrawing.xsddml-wordprocessingDrawing.xsdpml.xsdshared-additionalCharacteristics.xsdshared-bibliography.xsdshared-commonSimpleTypes.xsdshared-customXmlDataProperties.xsdshared-customXmlSchemaProperties.xsdshared-documentPropertiesCustom.xsdshared-documentPropertiesExtended.xsdshared-documentPropertiesVariantTypes.xsdshared-math.xsd详细介绍
DOCX 创建、编辑与分析
概述
.docx 文件是一个包含 XML 文件的 ZIP 压缩包。
快速参考
| 任务 | 方法 |
|---|---|
| 阅读/分析内容 | 使用 pandoc 或解压以查看原始 XML |
| 创建新文档 | 使用 docx-js——参见下文的“创建新文档” |
| 编辑现有文档 | 解压 → 编辑 XML → 重新打包——参见下文的“编辑现有文档” |
将 .doc 转换为 .docx
旧版 .doc 文件必须先转换才能编辑:
python scripts/office/soffice.py --headless --convert-to docx document.doc
阅读内容
# Text extraction with tracked changes
pandoc --track-changes=all document.docx -o output.md
# Raw XML access
python scripts/office/unpack.py document.docx unpacked/
转换为图像
python scripts/office/soffice.py --headless --convert-to pdf document.docx
pdftoppm -jpeg -r 150 document.pdf page
接受修订
要生成接受所有修订的干净文档(需要 LibreOffice):
python scripts/accept_changes.py input.docx output.docx
创建新文档
使用 JavaScript 生成 .docx 文件,然后进行验证。安装:npm install -g docx
设置
const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell, ImageRun,
Header, Footer, AlignmentType, PageOrientation, LevelFormat, ExternalHyperlink,
TableOfContents, HeadingLevel, BorderStyle, WidthType, ShadingType,
VerticalAlign, PageNumber, PageBreak } = require('docx');
const doc = new Document({ sections: [{ children: [/* content */] }] });
Packer.toBuffer(doc).then(buffer => fs.writeFileSync("doc.docx", buffer));
验证
创建文件后,请进行验证。如果验证失败,请解压、修复 XML,然后重新打包。
python scripts/office/validate.py doc.docx
页面大小
// CRITICAL: docx-js defaults to A4, not US Letter
// Always set page size explicitly for consistent results
sections: [{
properties: {
page: {
size: {
width: 12240, // 8.5 inches in DXA
height: 15840 // 11 inches in DXA
},
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } // 1 inch margins
}
},
children: [/* content */]
}]
常见纸张尺寸(DXA 单位,1440 DXA = 1 英寸):
| 纸张 | 宽度 | 高度 | 内容宽度(页边距为 1") |
|---|---|---|---|
| US Letter | 12,240 | 15,840 | 9,360 |
| A4(默认) | 11,906 | 16,838 | 9,026 |
横向页面: docx-js 会在内部交换宽度和高度,因此请传入纵向尺寸并让它处理交换:
size: {
width: 12240, // Pass SHORT edge as width
height: 15840, // Pass LONG edge as height
orientation: PageOrientation.LANDSCAPE // docx-js swaps them in the XML
},
// Content width = 15840 - left margin - right margin (uses the long edge)
样式(覆盖内置标题样式)
默认字体使用 Arial(普遍受支持)。标题保持黑色以确保易读。
const doc = new Document({
styles: {
default: { document: { run: { font: "Arial", size: 24 } } }, // 12pt default
paragraphStyles: [
// IMPORTANT: Use exact IDs to override built-in styles
{ id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true,
run: { size: 32, bold: true, font: "Arial" },
paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0 } }, // outlineLevel required for TOC
{ id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true,
run: { size: 28, bold: true, font: "Arial" },
paragraph: { spacing: { before: 180, after: 180 }, outlineLevel: 1 } },
]
},
sections: [{
children: [
new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun("Title")] }),
]
}]
});
列表(切勿使用 Unicode 项目符号)
// ❌ WRONG - never manually insert bullet characters
new Paragraph({ children: [new TextRun("• Item")] }) // BAD
new Paragraph({ children: [new TextRun("\u2022 Item")] }) // BAD
// ✅ CORRECT - use numbering config with LevelFormat.BULLET
const doc = new Document({
numbering: {
config: [
{ reference: "bullets",
levels: [{ level: 0, format: LevelFormat.BULLET, text: "•", alignment: AlignmentType.LEFT,
style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] },
{ reference: "numbers",
levels: [{ level: 0, format: LevelFormat.DECIMAL, text: "%1.", alignment: AlignmentType.LEFT,
style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] },
]
},
sections: [{
children: [
new Paragraph({ numbering: { reference: "bullets", level: 0 },
children: [new TextRun("Bullet item")] }),
new Paragraph({ numbering: { reference: "numbers", level: 0 },
children: [new TextRun("Numbered item")] }),
]
}]
});
// ⚠️ Each reference creates INDEPENDENT numbering
// Same reference = continues (1,2,3 then 4,5,6)
// Different reference = restarts (1,2,3 then 1,2,3)
表格
重要:表格需要双重宽度设置——既要在表格上设置 columnWidths,也要在每个单元格上设置 width。缺少其中任一项,表格在某些平台上的渲染都会不正确。
// CRITICAL: Always set table width for consistent rendering
// CRITICAL: Use ShadingType.CLEAR (not SOLID) to prevent black backgrounds
const border = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" };
const borders = { top: border, bottom: border, left: border, right: border };
new Table({
width: { size: 9360, type: WidthType.DXA }, // Always use DXA (percentages break in Google Docs)
columnWidths: [4680, 4680], // Must sum to table width (DXA: 1440 = 1 inch)
rows: [
new TableRow({
children: [
new TableCell({
borders,
width: { size: 4680, type: WidthType.DXA }, // Also set on each cell
shading: { fill: "D5E8F0", type: ShadingType.CLEAR }, // CLEAR not SOLID
margins: { top: 80, bottom: 80, left: 120, right: 120 }, // Cell padding (internal, not added to width)
children: [new Paragraph({ children: [new TextRun("Cell")] })]
})
]
})
]
})
表格宽度计算:
始终使用 WidthType.DXA——WidthType.PERCENTAGE 在 Google Docs 中会出错。
// Table width = sum of columnWidths = content width
// US Letter with 1" margins: 12240 - 2880 = 9360 DXA
width: { size: 9360, type: WidthType.DXA },
columnWidths: [7000, 2360] // Must sum to table width
宽度规则:
- 始终使用
WidthType.DXA——切勿使用WidthType.PERCENTAGE(与 Google Docs 不兼容) - 表格宽度必须等于
columnWidths之和 - 单元格的
width必须与对应的columnWidth一致 - 单元格的
margins是内部填充,会缩小内容区域,而不是增加单元格宽度 - 对于全宽表格:使用内容宽度(页面宽度减去左右页边距)
图像
// CRITICAL: type parameter is REQUIRED
new Paragraph({
children: [new ImageRun({
type: "png", // Required: png, jpg, jpeg, gif, bmp, svg
data: fs.readFileSync("image.png"),
transformation: { width: 200, height: 150 },
altText: { title: "Title", description: "Desc", name: "Name" } // All three required
})]
})
分页符
// CRITICAL: PageBreak must be inside a Paragraph
new Paragraph({ children: [new PageBreak()] })
// Or use pageBreakBefore
new Paragraph({ pageBreakBefore: true, children: [new TextRun("New page")] })
目录
// CRITICAL: Headings must use HeadingLevel ONLY - no custom styles
new TableOfContents("Table of Contents", { hyperlink: true, headingStyleRange: "1-3" })
页眉/页脚
sections: [{
properties: {
page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } } // 1440 = 1 inch
},
headers: {
default: new Header({ children: [new Paragraph({ children: [new TextRun("Header")] })] })
},
footers: {
default: new Footer({ children: [new Paragraph({
children: [new TextRun("Page "), new TextRun({ children: [PageNumber.CURRENT] })]
})] })
},
children: [/* content */]
}]
docx-js 的关键规则
- 明确设置页面大小——docx-js 默认使用 A4;美国文档请使用 US Letter(12240 x 15840 DXA)
- 横向页面:传入纵向尺寸——docx-js 会在内部交换宽度和高度;将短边作为
width、长边作为height传入,并设置orientation: PageOrientation.LANDSCAPE - 切勿使用
\n——请使用独立的 Paragraph 元素 - 切勿使用 Unicode 项目符号——请使用带编号配置的
LevelFormat.BULLET - PageBreak 必须放在 Paragraph 中——单独使用会生成无效 XML
- ImageRun 需要
type——始终指定 png/jpg 等类型 - 始终使用 DXA 设置表格的
width——切勿使用WidthType.PERCENTAGE(会导致 Google Docs 出错) - 表格需要双重宽度设置——
columnWidths数组和单元格的width都必须设置,且两者必须一致 - 表格宽度 =
columnWidths之和——对于 DXA,请确保总和完全一致 - 始终为单元格添加边距——使用
margins: { top: 80, bottom: 80, left: 120, right: 120 },以提供易读的填充 - 使用
ShadingType.CLEAR——表格底纹切勿使用 SOLID - 目录仅支持 HeadingLevel——标题段落上不要使用自定义样式
- 覆盖内置样式——使用精确的 ID:“Heading1”、“Heading2”等
- 包含
outlineLevel——目录必需(H1 为 0,H2 为 1,以此类推)
编辑现有文档
请按顺序完成以下 3 个步骤。
步骤 1:解压
python scripts/office/unpack.py document.docx unpacked/
提取 XML、进行美化格式处理、合并相邻的运行,并将智能引号转换为 XML 实体(“ 等),以确保它们在编辑过程中保留。使用 --merge-runs false 可跳过运行合并。
步骤 2:编辑 XML
编辑 unpacked/word/ 中的文件。模式请参见下方的 XML 参考。
除非用户明确要求使用其他姓名,否则修订和批注的作者请使用“Claude”。
请直接使用 Edit 工具替换字符串。不要编写 Python 脚本。 脚本会带来不必要的复杂性。Edit 工具会明确显示要替换的内容。
重要:新增内容请使用弯引号。 添加带撇号或引号的文本时,请使用 XML 实体生成弯引号:
<w:t>Here’s a quote: “Hello”</w:t>
| 实体 | 字符 |
|---|---|
‘ | ‘(左单引号) |
’ | ’(右单引号/撇号) |
“ | “(左双引号) |
” | ”(右双引号) |
添加批注: 使用 comment.py 在多个 XML 文件中处理样板内容(文本必须预先转义为 XML):
python scripts/comment.py unpacked/ 0 "Comment text with & and ’"
python scripts/comment.py unpacked/ 1 "Reply text" --parent 0 # reply to comment 0
python scripts/comment.py unpacked/ 0 "Text" --author "Custom Author" # custom author name
然后在 document.xml 中添加标记(参见 XML 参考中的“批注”)。
步骤 3:打包
python scripts/office/pack.py unpacked/ output.docx --original document.docx
通过自动修复进行验证、压缩 XML 并创建 DOCX。使用 --validate false 可跳过验证。
自动修复会修复:
durableId>= 0x7FFFFFFF(重新生成有效 ID)<w:t>中缺少xml:space="preserve"属性(适用于含空白字符的情况)
自动修复无法修复:
- 格式错误的 XML、无效的元素嵌套、缺失的关系、架构违规
常见问题
- 替换整个
<w:r>元素:添加修订时,将整个<w:r>...</w:r>块替换为同级的<w:del>...<w:ins>...。不要将修订标签插入某个 run 内部。 - 保留
<w:rPr>格式:将原 run 的<w:rPr>块复制到修订 run 中,以保留粗体、字号等格式。
XML 参考
架构合规
<w:pPr>中的元素顺序:<w:pStyle>、<w:numPr>、<w:spacing>、<w:ind>、<w:jc>,最后是<w:rPr>- 空白字符:对于前后带空格的
<w:t>,添加xml:space="preserve" - RSID:必须为 8 位十六进制数(例如
00AB1234)
修订
插入:
<w:ins w:id="1" w:author="Claude" w:date="2025-01-01T00:00:00Z">
<w:r><w:t>inserted text</w:t></w:r>
</w:ins>
删除:
<w:del w:id="2" w:author="Claude" w:date="2025-01-01T00:00:00Z">
<w:r><w:delText>deleted text</w:delText></w:r>
</w:del>
在 <w:del> 内部:使用 <w:delText> 替代 <w:t>,并使用 <w:delInstrText> 替代 <w:instrText>。
最小化编辑——仅标记发生更改的内容:
<w:r><w:t>The term is </w:t></w:r>
<w:del w:id="1" w:author="Claude" w:date="...">
<w:r><w:delText>30</w:delText></w:r>
</w:del>
<w:ins w:id="2" w:author="Claude" w:date="...">
<w:r><w:t>60</w:t></w:r>
</w:ins>
<w:r><w:t> days.</w:t></w:r>
删除整个段落/列表项——删除段落中的所有内容时,也要将段落标记为已删除,使其与下一段合并。在 <w:pPr><w:rPr> 内添加 <w:del/>:
<w:p>
<w:pPr>
<w:numPr>...</w:numPr>
<w:rPr>
<w:del w:id="1" w:author="Claude" w:date="2025-01-01T00:00:00Z"/>
</w:rPr>
</w:pPr>
<w:del w:id="2" w:author="Claude" w:date="2025-01-01T00:00:00Z">
<w:r><w:delText>Entire paragraph content being deleted...</w:delText></w:r>
</w:del>
</w:p>
如果 <w:pPr><w:rPr> 中没有 <w:del/>,接受修订后会留下一个空段落/列表项。
拒绝其他作者的插入——在其插入内容内嵌套删除标记:
<w:ins w:author="Jane" w:id="5">
<w:del w:author="Claude" w:id="10">
<w:r><w:delText>their inserted text</w:delText></w:r>
</w:del>
</w:ins>
恢复其他作者的删除——在其删除内容之后添加插入内容(不要修改其删除内容):
<w:del w:author="Jane" w:id="5">
<w:r><w:delText>deleted text</w:delText></w:r>
</w:del>
<w:ins w:author="Claude" w:id="10">
<w:r><w:t>deleted text</w:t></w:r>
</w:ins>
批注
运行 comment.py(参见步骤 2)后,在 document.xml 中添加标记。回复时,使用 --parent 标志,并将标记嵌套在父批注的标记中。
重要:<w:commentRangeStart> 和 <w:commentRangeEnd> 必须与 <w:r> 同级,绝不能位于 <w:r> 内部。
<w:commentRangeStart w:id="0"/>
<w:del w:id="1" w:author="Claude" w:date="2025-01-01T00:00:00Z">
<w:r><w:delText>deleted</w:delText></w:r>
</w:del>
<w:r><w:t> more text</w:t></w:r>
<w:commentRangeEnd w:id="0"/>
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:commentReference w:id="0"/></w:r>
<w:commentRangeStart w:id="0"/>
<w:commentRangeStart w:id="1"/>
<w:r><w:t>text</w:t></w:r>
<w:commentRangeEnd w:id="1"/>
<w:commentRangeEnd w:id="0"/>
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:commentReference w:id="0"/></w:r>
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:commentReference w:id="1"/></w:r>
图片
- 将图片文件添加到
word/media/ - 在
word/_rels/document.xml.rels中添加关系:
<Relationship Id="rId5" Type=".../image" Target="media/image1.png"/>
- 在
[Content_Types].xml中添加内容类型:
<Default Extension="png" ContentType="image/png"/>
- 在 document.xml 中引用:
<w:drawing>
<wp:inline>
<wp:extent cx="914400" cy="914400"/>
<a:graphic>
<a:graphicData uri=".../picture">
<pic:pic>
<pic:blipFill><a:blip r:embed="rId5"/></pic:blipFill>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
依赖项
- pandoc:文本提取
- docx:
npm install -g docx(新建文档) - LibreOffice:PDF 转换(通过
scripts/office/soffice.py自动配置,适用于沙盒环境) - Poppler:用于处理图片的
pdftoppm