mcpField
D
docx-processing-anthropic

docx-processing-anthropic

github.com/lawve-ai文档处理90786 次安装1 周前更新
代码仓库

关于 docx-processing-anthropic

面向需要创建、编辑或分析 DOCX 文件的用户,提供从文本提取到 XML 修改和文档验证的操作指南,并涵盖 docx-js 文档生成细节。

主要功能

  1. 01提取文档文本或原始 XML
  2. 02使用 docx-js 创建文档
  3. 03解包、修改并重新打包 DOCX
  4. 04处理修订、批注、图片和表格
  5. 05转换 PDF 与页面图像
  6. 06验证并修复文档结构

适用场景

  1. 01提取 DOCX 内容并保留修订
  2. 02创建并验证新的 DOCX 文件
  3. 03修改现有文档的 XML 内容
  4. 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.txt
scripts/
__init__.py
accept_changes.py
comment.py
scripts/office/
pack.py
scripts/office/helpers/
__init__.py
merge_runs.py
simplify_redlines.py
scripts/office/schemas/ecma/fouth-edition/
opc-contentTypes.xsd
opc-coreProperties.xsd
opc-digSig.xsd
opc-relationships.xsd
scripts/office/schemas/ISO-IEC29500-4_2016/
dml-chart.xsd
dml-chartDrawing.xsd
dml-diagram.xsd
dml-lockedCanvas.xsd
dml-main.xsd
dml-picture.xsd
dml-spreadsheetDrawing.xsd
dml-wordprocessingDrawing.xsd
pml.xsd
shared-additionalCharacteristics.xsd
shared-bibliography.xsd
shared-commonSimpleTypes.xsd
shared-customXmlDataProperties.xsd
shared-customXmlSchemaProperties.xsd
shared-documentPropertiesCustom.xsd
shared-documentPropertiesExtended.xsd
shared-documentPropertiesVariantTypes.xsd
shared-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 Letter12,24015,8409,360
A4(默认)11,90616,8389,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&#x2019;s a quote: &#x201C;Hello&#x201D;</w:t>
实体字符
&#x2018;‘(左单引号)
&#x2019;’(右单引号/撇号)
&#x201C;“(左双引号)
&#x201D;”(右双引号)

添加批注: 使用 comment.py 在多个 XML 文件中处理样板内容(文本必须预先转义为 XML):

python scripts/comment.py unpacked/ 0 "Comment text with &amp; and &#x2019;"
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>
图片
  1. 将图片文件添加到 word/media/
  2. 在 word/_rels/document.xml.rels 中添加关系:
<Relationship Id="rId5" Type=".../image" Target="media/image1.png"/>
  1. 在 [Content_Types].xml 中添加内容类型:
<Default Extension="png" ContentType="image/png"/>
  1. 在 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

评论

支持 Markdown