mcpField
D
docx

docx

github.com/agentscope-ai文档处理36.9k76 次安装1 天前更新
代码仓库

关于 docx

用于创建、编辑和分析 DOCX 文件的技能,适合涉及文本、XML 和版式检查的文档工作流。它为常见文档操作提供分步指导。

主要功能

  1. 01使用 docx-js 创建 DOCX
  2. 02提取文本并检查 XML
  3. 03通过 XML 编辑现有文件
  4. 04将 DOCX 转为 PDF 或图像
  5. 05接受修订并验证文件

适用场景

  1. 01生成新的 DOCX 文档
  2. 02提取或分析文档内容
  3. 03修改现有 DOCX 文件
  4. 04导出文档以进行视觉检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/agentscope-ai/qwenpaw --skill docx

安装后在 Claude Code 中输入/docx确认已加载。

包含的文件

除 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

详细介绍

重要: 所有 scripts/ 路径均相对于此 skill 目录。 运行方式:cd {this_skill_dir} && python scripts/... 或使用 execute_shell_command 的 cwd 参数。

DOCX 创建、编辑和分析

前置条件

  • docx(npm install -g docx):创建新文档
  • LibreOffice(soffice):.doc -> .docx 转换、接受修订和 PDF 导出
  • pandoc:文本提取
  • pdftoppm(poppler-utils):文档转图像工作流
  • 如果 pdftoppm 不可用,Python 回退路径可使用 pdf2image。
  • 在 Windows 上,依赖项必须已安装并在 PATH 中可用;如果缺失,请报告依赖问题并停止(不要持续重试)。

概述

.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/etc
  • 表格 width 始终使用 DXA 设置 - 切勿使用 WidthType.PERCENTAGE(在 Google Docs 中会出错)
  • 表格需要双重宽度 - columnWidths 数组和单元格 width,两者必须一致
  • 表格宽度 = columnWidths 之和 - 对于 DXA,确保它们精确相加
  • 始终添加单元格边距 - 使用 margins: { top: 80, bottom: 80, left: 120, right: 120 } 以获得可读的留白
  • 使用 ShadingType.CLEAR - 表格底纹切勿使用 SOLID
  • TOC 仅需 HeadingLevel - 标题段落上不要使用自定义样式
  • 覆盖内置样式 - 使用确切的 ID:"Heading1"、"Heading2" 等
  • 包含 outlineLevel - TOC 所必需(H1 为 0,H2 为 1,依此类推)

编辑现有文档

按顺序遵循全部 3 个步骤。

步骤 1:解包
python scripts/office/unpack.py document.docx unpacked/

提取 XML、格式化输出、合并相邻的 run,并将智能引号转换为 XML 实体(“ 等),以便它们在编辑后得以保留。使用 --merge-runs false 跳过 run 合并。

步骤 2:编辑 XML

编辑 unpacked/word/ 中的文件。有关模式请参见下方的 XML Reference。

使用 "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 Reference 中的 Comments)。

步骤 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 Reference

架构合规性
  • <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>

评论

支持 Markdown