返回全部 Skills

docx

其他 官方认证

每当用户想要创建、读取、编辑或操作Word文档(.docx文件)时,请使用此技能。触发条件包括:任何提及‘Word文档’、‘word document’、‘.docx’的情况,或要求生成带有目录、标题、页码、信头等格式的专业文档。当从.docx文件中提取或重新组织内容、在文档中插入或替换图片、在Word文件中执行查找替换、处理修订或批注、或将内容转换为精美的Word文档时,也应使用此技能。如果用户要求以Word或.docx文件形式提供‘报告’、‘备忘录’、‘信函’、‘模板’或类似交付物,请使用此技能。请**不要**用于PDF、电子表格、Google Docs或与文档生成无关的通用编程任务。

80.8k

下载量

AI SkillHub 能力展示图

安装方式

命令行安装

在项目根目录执行以下命令,完成 Skill 安装。

npx bzskills add anthropics/skills --skill docx

skill.md

name: docx
description: 每当用户想要创建、读取、编辑或操作Word文档(.docx文件)时,请使用此技能。触发条件包括:任何提及‘Word文档’、‘word document’、‘.docx’的情况,或要求生成带有目录、标题、页码、信头等格式的专业文档。当从.docx文件中提取或重新组织内容、在文档中插入或替换图片、在Word文件中执行查找替换、处理修订或批注、或将内容转换为精美的Word文档时,也应使用此技能。如果用户要求以Word或.docx文件形式提供‘报告’、‘备忘录’、‘信函’、‘模板’或类似交付物,请使用此技能。请**不要**用于PDF、电子表格、Google Docs或与文档生成无关的通用编程任务。
license: Proprietary. LICENSE.txt has complete terms

DOCX 创建、编辑与分析

概述

.docx 文件是一个包含 XML 文件的 ZIP 压缩包。

快速参考

任务方法
读取/分析内容pandoc 或解包后查看原始 XML
创建新文档使用 docx-js — 参见下方“创建新文档”
编辑现有文档解包 → 编辑 XML → 重新打包 — 参见下方“编辑现有文档”

将 .doc 转换为 .docx

旧版 .doc 文件在编辑前必须先转换:

python scripts/office/soffice.py --headless --convert-to docx document.doc

读取内容

# 带修订标记的文本提取
pandoc --track-changes=all document.docx -o output.md

# 原始 XML 访问
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,
        InternalHyperlink, Bookmark, FootnoteReferenceRun, PositionalTab,
        PositionalTabAlignment, PositionalTabRelativeTo, PositionalTabLeader,
        TabStopType, TabStopPosition, Column, SectionType,
        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

页面尺寸

// 重要:docx-js 默认使用 A4,而非 US Letter
// 始终显式设置页面尺寸以确保结果一致
sections: [{
  properties: {
    page: {
      size: {
        width: 12240,   // 8.5 英寸(DXA 单位)
        height: 15840   // 11 英寸(DXA 单位)
      },
      margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } // 1 英寸页边距
    }
  },
  children: [/* content */]
}]

常见页面尺寸(DXA 单位,1440 DXA = 1 英寸):

纸张宽度高度内容宽度(1 英寸页边距)
US Letter12,24015,8409,360
A4(默认)11,90616,8389,026

横向方向: docx-js 内部会交换宽度/高度,因此传入纵向尺寸,让它处理交换:

size: {
  width: 12240,   // 传入短边作为宽度
  height: 15840,  // 传入长边作为高度
  orientation: PageOrientation.LANDSCAPE  // docx-js 会在 XML 中交换它们
},
// 内容宽度 = 15840 - 左边距 - 右边距(使用长边)

样式(覆盖内置标题样式)

使用 Arial 作为默认字体(通用支持)。保持标题为黑色以保证可读性。

const doc = new Document({
  styles: {
    default: { document: { run: { font: "Arial", size: 24 } } }, // 12pt 默认
    paragraphStyles: [
      // 重要:使用准确的 ID 来覆盖内置样式
      { 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 } }, // TOC 需要 outlineLevel
      { 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 项目符号)

// ❌ 错误——绝不要手动插入项目符号字符
new Paragraph({ children: [new TextRun("• Item")] })  // 错误
new Paragraph({ children: [new TextRun("\u2022 Item")] })  // 错误

// ✅ 正确——使用带有 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")] }),
    ]
  }]
});

// ⚠️ 每个引用创建独立的编号
// 相同的引用 = 连续编号 (1,2,3 然后 4,5,6)
// 不同的引用 = 重新开始 (1,2,3 然后 1,2,3)

表格

重要:表格需要双重宽度 —— 同时设置表格的 columnWidths 和每个单元格的 width。如果缺少两者之一,表格在某些平台上会渲染错误。

// 重要:始终设置表格宽度以确保一致渲染
// 重要:使用 ShadingType.CLEAR(而非 SOLID)以防止黑色背景
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 }, // 始终使用 DXA(百分比在 Google Docs 中会出错)
  columnWidths: [4680, 4680], // 必须总和等于表格宽度(DXA: 1440 = 1 英寸)
  rows: [
    new TableRow({
      children: [
        new TableCell({
          borders,
          width: { size: 4680, type: WidthType.DXA }, // 也设置在每个单元格上
          shading: { fill: "D5E8F0", type: ShadingType.CLEAR }, // 用 CLEAR 而不是 SOLID
          margins: { top: 80, bottom: 80, left: 120, right: 120 }, // 单元格内边距(内部,不增加到宽度上)
          children: [new Paragraph({ children: [new TextRun("Cell")] })]
        })
      ]
    })
  ]
})

表格宽度计算:

始终使用 WidthType.DXA —— WidthType.PERCENTAGE 在 Google Docs 中会出错。

// 表格宽度 = columnWidths 之和 = 内容宽度
// US Letter 带 1 英寸页边距:12240 - 2880 = 9360 DXA
width: { size: 9360, type: WidthType.DXA },
columnWidths: [7000, 2360]  // 必须总和等于表格宽度

宽度规则:

  • 始终使用 WidthType.DXA —— 绝不要使用 WidthType.PERCENTAGE(与 Google Docs 不兼容)
  • 表格宽度必须等于 columnWidths 之和
  • 单元格 width 必须匹配对应的 columnWidth
  • 单元格 margins 是内部填充——它们会减少内容区域,而不是增加单元格宽度
  • 对于全宽表格:使用内容宽度(页面宽度减去左右页边距)

图片

// 重要:type 参数是必需的
new Paragraph({
  children: [new ImageRun({
    type: "png", // 必需:png, jpg, jpeg, gif, bmp, svg
    data: fs.readFileSync("image.png"),
    transformation: { width: 200, height: 150 },
    altText: { title: "Title", description: "Desc", name: "Name" } // 三个都必须
  })]
})

分页符

// 重要:PageBreak 必须放在 Paragraph 内部
new Paragraph({ children: [new PageBreak()] })

// 或者使用 pageBreakBefore
new Paragraph({ pageBreakBefore: true, children: [new TextRun("New page")] })

超链接

// 外部链接
new Paragraph({
  children: [new ExternalHyperlink({
    children: [new TextRun({ text: "Click here", style: "Hyperlink" })],
    link: "https://example.com",
  })]
})

// 内部链接(书签 + 引用)
// 1. 在目标位置创建书签
new Paragraph({ heading: HeadingLevel.HEADING_1, children: [
  new Bookmark({ id: "chapter1", children: [new TextRun("Chapter 1")] }),
]})
// 2. 链接到它
new Paragraph({ children: [new InternalHyperlink({
  children: [new TextRun({ text: "See Chapter 1", style: "Hyperlink" })],
  anchor: "chapter1",
})]})

脚注

const doc = new Document({
  footnotes: {
    1: { children: [new Paragraph("Source: Annual Report 2024")] },
    2: { children: [new Paragraph("See appendix for methodology")] },
  },
  sections: [{
    children: [new Paragraph({
      children: [
        new TextRun("Revenue grew 15%"),
        new FootnoteReferenceRun(1),
        new TextRun(" using adjusted metrics"),
        new FootnoteReferenceRun(2),
      ],
    })]
  }]
});

制表位

// 在同一行右对齐文本(例如,日期与标题相对)
new Paragraph({
  children: [
    new TextRun("Company Name"),
    new TextRun("\tJanuary 2025"),
  ],
  tabStops: [{ type: TabStopType.RIGHT, position: TabStopPosition.MAX }],
})

// 前导点(例如,目录样式)
new Paragraph({
  children: [
    new TextRun("Introduction"),
    new TextRun({ children: [
      new PositionalTab({
        alignment: PositionalTabAlignment.RIGHT,
        relativeTo: PositionalTabRelativeTo.MARGIN,
        leader: PositionalTabLeader.DOT,
      }),
      "3",
    ]}),
  ],
})

多栏布局

// 等宽栏
sections: [{
  properties: {
    column: {
      count: 2,          // 栏数
      space: 720,        // 栏间距,DXA 单位(720 = 0.5 英寸)
      equalWidth: true,
      separate: true,    // 栏间垂直分隔线
    },
  },
  children: [/* 内容自然跨栏流动 */]
}]

// 自定义宽度栏(equalWidth 必须为 false)
sections: [{
  properties: {
    column: {
      equalWidth: false,
      children: [
        new Column({ width: 5400, space: 720 }),
        new Column({ width: 3240 }),
      ],
    },
  },
  children: [/* 内容 */]
}]

通过使用 type: SectionType.NEXT_COLUMN 的新节来强制分栏。

目录

// 重要:标题必须仅使用 HeadingLevel —— 不要使用自定义样式
new TableOfContents("Table of Contents", { hyperlink: true, headingStyleRange: "1-3" })

页眉/页脚

sections: [{
  properties: {
    page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } } // 1440 = 1 英寸
  },
  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
  • 绝不要使用表格作为分隔线/横线 —— 单元格有最小高度并会渲染为空框(包括在页眉/页脚中);应使用 border: { bottom: { style: BorderStyle.SINGLE, size: 6, color: "2E75B6", space: 1 } } 放在 Paragraph 上。对于双栏页脚,使用制表位(参见制表位部分),不要使用表格
  • TOC 仅需要 HeadingLevel —— 标题段落上不要使用自定义样式
  • 覆盖内置样式 —— 使用准确的 ID:"Heading1"、"Heading2" 等
  • 包含 outlineLevel —— TOC 需要(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  # 回复批注 0
python scripts/comment.py unpacked/ 0 "Text" --author "Custom Author"  # 自定义作者名称

然后将标记添加到 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>...。不要将修订标记标签注入到文本段内部。
  • 保留 <w:rPr> 格式:将原始文本段的 <w:rPr> 块复制到您的修订标记文本段中,以保持粗体、字号等。

---

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>

最小化编辑 —— 仅标记变化的内容:

<!-- 将“30 days”改为“60 days” -->
<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:p 的直接子元素,绝不在 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>

<!-- 批注 0 及其内部嵌套的回复 1 -->
<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"/>  <!-- EMUs: 914400 = 1 英寸 -->
    <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:文本提取
  • docxnpm install -g docx(新文档)
  • LibreOffice:PDF 转换(通过 scripts/office/soffice.py 为沙盒环境自动配置)
  • Popplerpdftoppm 用于图片