cycleuser/skills

docx-editor

Lossless DOCX unpack / edit / repack toolkit. Treats a .docx as a ZIP of OOXML parts, edits the raw XML with lxml, and on save copies every UNMODIFIED part byte-for-byte — only the parts you actually edited are re-serialised. Guarantees that opening, editing and saving a document leaves its formatting completely intact. Triggers when: Editing a .docx while preserving format exactly (改文字但不改格式), unpacking a .docx into a folder of raw XML/media/files then repacking it back, swapping an emb…

First seen Aug 17, 2026

Installation

$ npx skills add cycleuser/skills --skill docx-editor

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from cycleuser/skills · top by installs.

npx skills add cycleuser/skills

Browse all from cycleuser/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 12
License LICENSE
Default branch main
Open issues 0
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.0.0
LicenseMIT

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 14,020 B
  • docs README.md 5,801 B
  • docs SUMMARY.md 2,504 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 2 installs

SKILL.md

Safety Rules

参见 [shared/core/safety-rules.md](../shared/core/safety-rules.md) — 所有安全规则从共享层加载,避免跨技能重复维护。

关键补充:生产环境不覆盖原文件。所有编辑输出写为新文件(<原名>-edited.docx 或用户指定路径)。原地保存 doc.save() 虽走临时文件+原子移动,但仍属破坏性操作,仅在用户明确要求时使用。

Docx 文档编辑 (DocxEditor / Lossless DOCX Editor)

严格"只改要改的,其余一字不动"的 DOCX 编辑技能。核心原则:把 .docx 当 ZIP 容器,用 lxml 直接操作里面的原始 XML,保存时未修改的部件逐字节复制,只有被改动的部件才重新序列化。绝不用 python-docx 重新生成整个文档——那样会让每个被加载的部件都被重写,导致格式漂移和"打开→保存"不再幂等。

The one rule that matters: a .docx is a ZIP of OOXML parts. Edit only the parts you mean to change; on save, copy every other part byte-for-byte from the original. Never regenerate the whole document.

Quick Commands

Command 说明 / Description
/docx编辑 <文件> 用自然语言描述要改什么,技能生成编辑脚本并执行
/docx编辑 unpack <文件> [目录] 解包 .docx 到目录(每个 part 原样落盘)
/docx编辑 pack <目录> <输出.docx> 从目录重新打包成 .docx
/docx编辑 dump <文件> 打印段落与表格结构
/docx编辑 replace <文件> <旧> <新> [-o 输出] 全局查找替换(跨 run,保留格式)
/docx编辑 set-para <文件> <序号> <文字> [-o 输出] 按序号重写主文档段落
/docx编辑 set-cell <文件> <表> <行> <列> <文字> [-o 输出] 重写表格单元格
/docx编辑 parts <文件> [--bytes] 列出所有部件(可加字节大小)
/docx编辑 find <文件> <关键词> 查找包含关键词的段落
/docx编辑 gui [文件] 启动 PySide6 图形界面
/docxedit <file> English alias for the editor
/docxedit unpack <file> [dir] Unpack a .docx into a folder
/docxedit pack <dir> <out.docx> Repack a folder into a .docx
/docxedit gui [file] Launch the PySide6 GUI

核心理念 / Core Philosophy

从一次真实的"改个字却毁了整个文档格式"的事故中提炼:用 python-docx 打开一个精心排版的 docx,改一个段落文字,保存——结果字体、字号、行距、表格边框、页眉页脚全变了,因为 python-docx 把整个文档对象模型重新序列化了。

四条铁律:

  1. 不重写没改的.docx 是 ZIP,每个 entry(document.xmlstyles.xmlheader1.xmlfootnotes.xmlcomments.xml、图片...)都是独立部件。只重新序列化你真正改过的部件,其余从原始字节缓存逐字节回写。
  2. 惰性解析。只有当你访问某个 part 时才用 lxml 解析它。只读(如读段落文字)不触发 dirty。解析后的树被修改,必须显式 mark_dirty(name)
  3. 保留 ZIP 元信息。保存时沿用每个 entry 的原始 date_time,条目顺序与原文件一致——不排序、不重排。
  4. 克隆而非新建。增删表格行列、插入段落时,深拷贝已有元素作为模板(含 rPr/pPr/tcPr/gridSpan/vMerge 全部属性),绝不用 etree.SubElement 从零建——那样会丢光格式。

四步工作流 / Four-Step Workflow

Step 1  打开 Open
   DocxEditor(path) 把 docx 当 ZIP 读入,缓存每个 entry 的原始字节到 _raw 字典。
   不解析 XML,直到你真正访问某个 part 才惰性解析到 _trees。

Step 2  编辑 Edit(三条路径,按改动面由小到大)
   a) 高层助手(推荐)— replace_text / Paragraph.set_text / Cell.set_text /
      Table.add_row / Table.delete_column / Paragraph.insert_paragraph_after /
      Paragraph.style。只改必要元素,自动标记 dirty。
   b) 直接操作 lxml — doc.xml("word/footnotes.xml") 拿到根元素后直接改
      t.text = ...;必须手动 doc.mark_dirty("word/footnotes.xml")。
   c) 整体替换部件字节 — doc.set_part_bytes("word/media/image1.png", png_bytes),
      用于换图片/字体等二进制部件,不经过 lxml。

Step 3  保存 Save
   doc.save(out) 或 doc.save()(原地覆盖,走临时文件+原子移动)。
   dirty 集合里的 XML 部件重新序列化;其余从 _raw 逐字节回写;
   ZIP 条目顺序与原文件一致。新部件(dirty 里但不在原 names 中)追加。

Step 4  校验 Verify
   重新打开输出文件,确认编辑生效;对比原始与输出的部件字节,
   确认只有预期 part 改了,其余全部 byte-identical。

关键实现要点 / Key Implementation Notes

  • dirty 集合是唯一真相save() 只重写 self.dirty 中的 part。高层助手自动标记;直接改 lxml 元素后必须 doc.markdirty(name)
  • 跨 run 替换replace_text 把段落所有 <w:t> 拼成完整文本定位匹配,替换文字落入首个命中 run(继承其格式),其余命中 run 只删匹配部分。OOXML 常把一个词拆到多个 run,简单按 run 替换会漏匹配。
  • 段落重写保格式Paragraph.set_text 把新文字写进第一个有 <w:t> 的 run(保留它的 rPr:字体/字号/颜色/粗斜体),清空其余 run;段落 pPr(样式/对齐/缩进/编号)不动。
  • 表格行列克隆Table.addrow/addcolumncopy.deepcopy 复制最后一行/列(含 tcPr/gridSpan/vMerge/底纹),清空文字但保留段落和 run 骨架。
  • 页眉/页脚/脚注/尾注/批注:这些 part 不是 document.xml,高层助手不直接覆盖。统一用 doc.xml(name) + 改元素 + doc.mark_dirty(name)。页脚的页码域(PAGE/NUMPAGES)不要动 instrText/fldChar,只改域前后的文字 <w:t>
  • 换图片/二进制doc.setpartbytes("word/media/image1.png", new_bytes) 走原始字节替换,不经过 lxml,对二进制最安全。
  • 解包/打包unpackzipfile.extractall 原样解压;repack[Content_Types].xml 写在首位(Word 要求),其余按稳定排序。解包→重新打包后每个 part 内容字节与原文件一致。

详见 [scripts/docxmodskill.py](scripts/docxmodskill.py) —— 单文件可嵌入的完整引擎(约 480 行,依赖 lxml)。 GUI 见 [scripts/docxmodgui.py](scripts/docxmodgui.py) —— PySide6 界面(约 360 行)。

Rules

  • [rules/workflow.md](rules/workflow.md) - 完整四步工作流与命令行操作
  • [rules/format-preservation.md](rules/format-preservation.md) - 无损保格式的具体 OOXML 技术(字节缓存、dirty 集合、跨 run 替换、克隆增删)
  • [rules/editable-targets.md](rules/editable-targets.md) - 所有可编辑部件的定位方法与代码示例(段落/表格/页眉/脚注/批注/图片/属性)
  • [rules/edge-cases.md](rules/edge-cases.md) - 边界情况(合并单元格、嵌套表格、页码域、命名空间)与排错表

适用场景 / Supported Scenarios

  • 改文档里的文字但不能让格式有任何变化(如改合同金额、改报告年份、改名字)
  • 全局查找替换(含跨 run 的词、含页眉页脚脚注里的词)
  • 编辑表格单元格、增删行列(保留表格样式)
  • 编辑页眉/页脚/脚注/尾注/批注的文字
  • 换文档里的图片、签名、Logo(保留其他一切)
  • 批量修改文档属性(标题、作者)
  • 解包 .docx 到目录,用任意工具改原始 XML/图片,再重新打包
  • 需要确认"只有我改的那一处变了,其余逐字节一致"的审计场景

边界情况 / Edge Cases

  • 合并单元格Table.cell(r,c) 按物理 tc 索引,col_count() 是最大物理 tc 数,与逻辑列数可能不同。用 rows[r].findall(w("tc")) 按物理定位。
  • 表格内段落doc.paragraphs 只含 body 直属段落。表格内用 doc.all_paragraphs()cell.paragraphs
  • 页码域:页脚的 PAGE/NUMPAGES 是域代码,只改域前后的文字 <w:t>,不要动 instrText/fldChar
  • 命名空间docProps/core.xmldc 命名空间,不是 w[Content_Types].xml 用包内容类型命名空间。
  • 大文件DocxEditor 一次性读入整个 ZIP 到内存。对极大 docx 先 unpack 解包,改完再 repack 更省内存。

常见问题排查 / Troubleshooting

  • 保存后编辑丢失 → 直接改了 lxml 元素但没 markdirty。在 save() 前调 doc.markdirty(part_name)
  • 保存后所有部件都变了 → 用了 python-docx 的 Document.save() 而非本技能的 DocxEditor.save()。改用 from docxmod import DocxEditor
  • 页眉/脚注/批注改了没生效 → 这些 part 不被 replacetext 覆盖;直接改 XML 后必须 markdirty
  • Word 打开报损坏 → 改了 [ContentTypes].xml 但没加对 override;或删了单元格唯一段落(单元格必须至少有一段,用 settext("") 而非 delete())。
  • 跨 run 替换没命中replace_text 已处理跨 run,但若 run 被 <w:proofErr> 打断可能漏。检查 XML,必要时 unpack 手工处理再 repack

验证 / Verification

本技能自带测试 [tests/testdocxmod.py](tests/testdocxmod.py)(约 280 行,无需外部样本,自建最小合法 docx):

python tests/test_docxmod.py    # 直接运行
# 或
pytest tests/test_docxmod.py -v

覆盖 16 项检查:解包↔打包字节级一致、打开→保存(不改)无损、全局替换、跨 run 替换、段落重写、段落增删、表格单元格、表格行列增删、段落样式、页眉编辑、脚注编辑、批注编辑、部件字节替换、原地保存、查找、原文件不被破坏。

依赖 / Dependencies

  • 必需lxmlpip install lxml
  • 可选PySide6(GUI,pip install PySide6
  • 测试pytest(可选,测试脚本也可直接 python 运行)
  • 依赖 _shared/core/safety-rules.md

文件结构

skills/docx-editor/
├── README.md
├── SKILL.md
├── rules/
│   ├── workflow.md              # 四步工作流与命令行操作
│   ├── format-preservation.md   # 无损保格式的 OOXML 技术
│   ├── editable-targets.md      # 所有可编辑部件的定位与示例
│   └── edge-cases.md            # 边界情况与排错
├── scripts/
│   ├── docxmod_skill.py         # 单文件引擎(核心,约 480 行)
│   └── docxmod_gui.py           # PySide6 GUI(约 360 行)
└── tests/
    └── test_docxmod.py          # 自验证测试(约 280 行)

版本历史 / Version History

版本 日期 变更
1.0.0 2026-08-16 初始版本:无损 DOCX 引擎、CLI、PySide6 GUI、四步工作流、16 项自验证测试

See Also / 相关技能

  • /填表 from tianbiao — 照固定模板填数据(本技能侧重"改已有内容",填表侧重"往空模板里填")
  • /公文 from official-document-writer — 从零撰写公文正文(本技能不生成内容,只编辑已有文档)