Imported from richenlin/HuashanAICode (
apps/desktop/resources/bundled-skills/docx/SKILL.md). Install upstream withnpx skills add richenlin/HuashanAICode --skill docx. Copyright stays with the author.
Word 文档创作与编辑
目标
用 docx-js(Node)创建、编辑、分析专业 Word 文档:报告、学术论文、合同、简历、公文、试卷、文案等。.docx 本质是 ZIP 压缩包内的 XML 文件集合。核心是产出在 MS Office / WPS 中无兼容性问题的文档:正确的封面、目录、页眉页脚、页码、跨页表格与字体设置。
工具链
- 创建/编辑:
docxnpm 包(npm install docx或bun add docx),写 JS 脚本生成。 - 批注:python-docx 处理简单批注;复杂批注需直接操作 XML。
- 文本提取:pandoc。
- PDF 转换 / .doc 支持:LibreOffice(
soffice --headless --convert-to pdf)。 - PDF 转图片:Poppler(
pdftoppm)。 - 安全解析 XML:defusedxml。
快速路由
- 判断任务类型 → 选路线(下表)
- 判断业务场景 → 套场景模板(下表)
- 选配色 mood + 封面配方(见"配色与封面"节)
- 按版式规范生成
- 生成后跑双层验证(见"后置验证"节)
任务路由
| 用户意图 | 路线 | 要点 |
|---|---|---|
| 新建(无附件) | 创建 | 先定大纲(用户给了大纲就严格照做,不增删不改序),再写 docx-js 代码 |
| 编辑/修改(有附件) | 编辑 | 尽量保留原格式,改动最小化;必要时直接操作 OOXML |
| 排版/字体/页边距 | 格式 | 按场景字体档案 + 版式规范 |
| 批注/审阅 | 批注 | python-docx 或 XML 注入 comments 部件 |
| 读取/分析/提取 | 读取 | pandoc 提取文本;保留结构信息 |
场景路由
| 关键词 | 场景 | 要点 |
|---|---|---|
| thesis、论文、学术、期刊 | 学术 | 封面(独立节)→ 摘要 → 目录 → 正文 → 参考文献;公式用 LaTeX 语法 |
| report、分析、实验、调研、总结、方案、可行性 | 报告 | 按类型选模板 A–F(见"大纲模板") |
| contract、协议、NDA、条款 | 合同 | 完整条款闭合、双方信息、签署区、统一【】占位符 |
| resume、简历、求职 | 简历 | 1.15 倍行距、微软雅黑/Calibri、无封面无目录 |
| exam、试卷、测验、教案 | 试卷 | 纯黑白、层级编号、选项缩进、预留作答区 |
| 公文、通知、函、纪要、红头 | 公文 | GB/T 9704 红头规范(仅用户要求时加红头) |
| 广播稿、产品文案、直播稿、演讲稿 | 文案 | 行距 400、视觉字体档案 |
版式规范(始终生效)
单位速查
| 单位 | 值 |
|---|---|
| 1 cm | 567 twips |
| 1 inch | 1440 twips |
| 1 pt | 20 half-points(docx-js size 单位) |
| A4 | 11906 × 16838 twips |
默认页面(A4 纵向)
宽 11906、高 16838;上/下边距 1440(2.54cm)、左边距 1701(3cm)、右边距 1417(2.5cm)。场景覆盖:公文(GB/T 9704)上 2098 下 1984 左 1588 右 1474;试卷四边 1134(2cm)。
字体档案
档案 A(正式:报告/学术/合同/公文/试卷):H1 黑体 16pt(32) 加粗居中;H2 黑体 15pt(30) 加粗;H3 黑体 14pt(28) 加粗;正文 宋体 12pt(24);图表题注 宋体 10.5pt(21)。英文一律 Times New Roman。文字颜色纯黑 "000000"。首行缩进 480 twips(宋体 12pt 两字符)。行距 312(1.3 倍)。
档案 B(视觉:简历/文案):微软雅黑/Calibri,正文 10–11pt,题注 9pt;首行缩进 420 twips;颜色用配色的 primary/accent。
公文覆盖(needsRedHeader):机关名 华文中宋(或宋体加粗) 26pt(52);标题 华文中宋(或黑体) 22pt(44);正文 仿宋 16pt(32);小标题 仿宋_GB2312 加粗(或黑体) 16pt(32)。行距 560(28pt 固定值),首行缩进 640 twips。
颜色路由:短篇文字类(书信、评论、申请、演讲稿等)标题一律纯黑;只有需要品牌/专业身份的文档(带封面的报告、白皮书、提案、咨询交付物)才用 palette.primary。
中文字号速查
| 字号 | pt | size | 字号 | pt | size |
|---|---|---|---|---|---|
| 初号 | 42 | 84 | 三号 | 16 | 32 |
| 小初 | 36 | 72 | 小三 | 15 | 30 |
| 一号 | 26 | 52 | 四号 | 14 | 28 |
| 小一 | 24 | 48 | 小四 | 12 | 24 |
| 二号 | 22 | 44 | 五号 | 10.5 | 21 |
| 小二 | 18 | 36 | 小五 | 9 | 18 |
表格 / 图片铁律
- 表格必须设
margins;底纹用ShadingType.CLEAR。 - 跨页表格:表头行
tableHeader: true,所有行cantSplit: true,标题段keepNext: true。 - 整页表格行高用
rule: "exact"(绝不用atLeast),留 1200 twips 安全余量。 - 图片必须传
type参数;用image-size保持纵横比,绝不硬编码宽高两个值。 - 分页符放在 Paragraph 内部,不放段落外。
占位符约定
缺失信息用全角括号 【 】 占位,方便用户查找替换,例如 【公司名称】、【人民币大写】、【____/____/____】。全文档格式一致;绝不写"TBD""待完善"这类含糊占位。
标题孤字防护
正文标题与封面标题都不能出现末行只有 1–2 个字的孤行。封面标题用 calcTitleLayout() + splitTitleLines()(见封面节);正文标题长到要换行时,在语义边界手动拆多个 TextRun + Break。
配色与封面
配色(mood 三维系统)
颜色由三个维度推导:温度(暖↔冷)、重量(轻↔重)、能量(静↔动)。每个文档 5 个 token:
| Token | 角色 |
|---|---|
| primary | 标题、封面题字(深色权威,由温度+重量推出) |
| body | 正文(近黑,高对比) |
| secondary | 题注、脚注(中灰) |
| accent | 表头、线条、链接("性格色",反映能量) |
| surface | 表格交替行、卡片背景(accent 极浅色) |
常用配方(示例):学术 Deep Sea primary#162032 accent#8B7E5A;法律 Legal Wood #28201C/#7A5C3A;科技 Dawn Mist Tech #0A1628/#5B8DB8;教育 Warm Sun #2A3518/#D4A030;默认 Plain Paper #101820/#8090A0;咨询 Terracotta #241E1A/#B08050;医疗 Mint Medical #0E2030/#3888A8;极简 White Porcelain #303030/#B89870;AI 渐变 Lapis Tech #1A1F36/#667eea;金融深蓝金 #0F2027/#D4AF37。
场景映射:学术→Deep Sea;普通报告→Plain Paper;咨询报告→Terracotta;科技报告→Dawn Mist Tech;合同→Legal Wood;简历→White Porcelain;试卷/公文→纯黑白;医疗→Mint Medical。
封面配方(强制)
有封面的文档(报告、论文、方案、白皮书、提案等)必须使用 7 个验证过的配方之一(R1–R7),禁止自由发挥封面代码——自由写封面必然出现空白页、边框丢失、溢出等兼容问题。
配方路由(selectCoverRecipe):
| docType | 配方 | 默认配色 |
|---|---|---|
| contract / official / exam / resume | 无封面 | — |
| academic / proposal_report(开题报告) | R5 学术白 | ACADEMIC |
| lesson_plan(STEM) | R4 顶部色块 | DM-1 |
| lesson_plan(文科/通用) | R6 编辑暖调 | ED-1 |
| creative / branding / design | R3 居中卡片框 | SN-2 |
| cultural / newsletter / internal / activity | R6 编辑暖调 | ED-1 |
| trend/research(文娱/创意/品牌) | R7 瑞士科技 | ST-1 |
| whitepaper | R2 双线框 | IG-1 / CM-2 |
| consulting | R2 双线框 | MIN-1 |
| proposal / plan | R4 顶部色块 | GO-1 |
| report | R1 纯段落左对齐 | 按行业 |
| 默认 | R1 | DS-1 |
长标题回退:R3/R4/R6 标题 >20 字 → 回退 R1;R2 标题 >30 字 → 回退 R1;R5 永不被覆盖。
配方架构(所有配方通用):单一 16838 高外包表格(一行,exact 高度);R1/R2/R3 零嵌套表格,装饰全部用段落边框实现(跨 MS Office + WPS 最稳)。封面标题字号必须用 calcTitleLayout() 动态计算(默认 40pt 起、最小 24pt,按可用宽度折行),禁止硬编码 40pt 以上字号;间距必须用 calcCoverSpacing() 按内容元素数动态计算,禁止固定大间距。封面内容总高 ≤ 15638 twips。深色背景上的每个 TextRun 必须显式设 color。封面节末尾不得有分页符或空段落。标题行按语义边界断行,不得出现单字孤行。禁止用文字装饰线(───、━━━),一律用段落边框。
用户提供了参考模板(PDF/docx)时:进入模板跟随模式,按模板结构复刻(每个独立页面类型 = 独立节:封面节边距 0、正文节标准边距、附录节可不同),不套用 R1–R7;但 common-rules 的兼容性约束(外包表格 allNoBorders、行距规则)仍适用。
目录(TOC)完整规则
三步流程,缺一不可:① docx-js 生成空目录域结构 → ② 后处理填充可见占位条目 → ③ 用户打开 Word 右键"更新域"得到真实页码。
什么时候加目录:3 个以上 H1 大节的文档。简历、试卷、短文、合同(<20 条款)不加。
代码侧 4 元素(顺序固定)
- 目录标题段:居中、加粗、
size: 32、黑体——禁止用 HeadingLevel(否则目录索引自己)。 new TableOfContents("Table of Contents", { hyperlink: true, headingStyleRange: "1-3" })(第一个参数只是内部名)。- 刷新提示段(强制):斜体灰字(size 18, color 888888)提示用户右键目录 →"更新域"刷新页码。
- 目录后强制 PageBreak——缺了它目录和正文会挤在同一页,这是最常见的目录失败原因。
正文标题必须用 heading: HeadingLevel.HEADING_X,禁止用"加粗+大字号"模拟;封面标题可以不用 Heading 样式(不进目录)。
页码三段式
封面(无页码)→ 前置部分(罗马数字 i,ii,iii,start=1)→ 正文(阿拉伯数字 1,2,3,start=1)。目录必须独占一节,正文节设置 page: { pageNumbers: { start: 1, formatType: NumberFormat.DECIMAL } }(pageNumbers 必须嵌在 page: {} 里)。后处理页脚:罗马节 instrText 必须含 PAGE \* ROMAN \* MERGEFORMAT,阿拉伯节含 PAGE \* arabic \* MERGEFORMAT(WPS 忽略 pgNumType fmt)。绝不在 instrText 里写 \* decimal——那是 docx-js API 枚举值不是 Word 域开关,会导致页码渲染成 "1decimal"。同时后处理删除封面节空的 <w:pgNumType/>(docx-js 会发出空元素,WPS 会困惑)。
引号转义(最常见生成 bug)
JS 字符串里的中文弯引号 “” ‘ ’ 必须用 Unicode 转义 \u201c \u201d \u2018 \u2019;直引号用 \" \' 或换分隔符。中文文本到处是弯引号强调(如"双11"、"前低后高"),漏一个就是 JS 语法错误,整个生成脚本静默失败。写任何含中文的字符串前先扫描一遍。
大纲模板
- 报告:analysis → 执行摘要→背景→范围与方法→发现→诊断→结论;experiment → 摘要→目标与假设→环境→过程→结果→误差分析→结论;testing → 概述→范围与环境→测试计划→结果→缺陷→风险→结论;research → 摘要→背景→对象与方法→样本→发现→综合→建议;review → 概述→目标→回顾→结果→问题→经验→行动计划;proposal → 摘要→现状→目标→方案→路线图→资源→风险→收益。
- 合同:bilateral → 首部→双方→鉴于→定义→标的→价款→权利→交付→税务→知识产权→违约→不可抗力→终止→通知→争议→其他→签署;transfer / nda / framework / terms 各有对应模板。
- 公文:notice → 红头→文号→标题→主送→缘由→事项→要求→附件→落款→日期→版记;letter / reply / minutes 各有对应模板。
公式与图表
- 公式:输入 LaTeX 语法。基础公式(分式、上下标、根式、求和)转 docx-js Math 组件;复杂公式(3 层以上嵌套、矩阵、分段函数)用 matplotlib 生成 PNG 嵌入。
- 图表:默认 matplotlib 模板库生成 PNG(柱状/折线/饼/箱线/雷达/热力图),颜色自动取文档配色 accent,默认莫兰迪低饱和。
后置验证(生成后必做)
手动清单(生成时自查)
- 行距 312(或场景覆盖值);CJK 正文 2 字符缩进(480/420)。
- 表格有 margins、ShadingType.CLEAR、表头 tableHeader、行 cantSplit。
- 图片有 type 参数、纵横比保持;PageBreak 在 Paragraph 内;编号列表 reference 唯一。
- 中文弯引号全部转义(见上)。
- 页眉页脚存在(至少页码);正文标题全部 Heading 样式。
- 空白页三规则:① section(NEXT_PAGE) 前一段不得以 PageBreak 结尾(双断=空白页)② 分页符段尽量含可见文本(封面后空段+PageBreak 是允许的例外)③ 连续空段落 ≤3。
- 目录:有"目录"标题必有 TableOfContents;目录后必跟 PageBreak;页码三段式正确;instrText 用
\* arabic/\* ROMAN。 - 封面:配方 R1–R7、calcTitleLayout、calcCoverSpacing、无溢出、无尾部 PageBreak。
脚本化检查
生成后用 Python 做自动检查(内联轻量版,检查核心业务规则):
#!/usr/bin/env python3
"""docx 后置检查(轻量版):空白页/图片纵横比/表格跨页/行距/节属性。"""
import sys, zipfile
from xml.etree import ElementTree as ET
W = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}"
def q(t): return W + t
path = sys.argv[1]
errors, warns = [], []
with zipfile.ZipFile(path) as z:
names = set(z.namelist())
if "word/document.xml" not in names:
sys.exit("不是有效 docx")
root = ET.fromstring(z.read("word/document.xml"))
body = root.find(q("body"))
paras = body.findall(".//" + q("p"))
empty_run = 0
for p in paras:
texts = "".join(t.text or "" for t in p.findall(".//" + q("t")))
has_break = p.find(".//" + q("br")) is not None
if not texts.strip() and not has_break:
empty_run += 1
else:
if empty_run > 3: warns.append(f"连续空段 {empty_run} 个(疑似空白页)")
empty_run = 0
for p in paras:
spacing = p.find(q("pPr") + "/" + q("spacing"))
if spacing is not None and spacing.get(q("line")) and spacing.get(q("line")) not in ("312", "560", "400"):
warns.append(f"行距异常 line={spacing.get(q('line'))}")
break
for pic in body.findall(".//" + q("drawing")):
ext = pic.find(".//" + q("extent"))
if ext is not None:
cx, cy = int(ext.get("cx")), int(ext.get("cy"))
if cx > 0 and cy > 0 and (cx / cy > 6 or cy / cx > 6):
warns.append(f"图片疑似失真 {cx}x{cy} twips")
for tbl in body.findall(".//" + q("tbl")):
if tbl.find(".//" + q("tblHeader")) is None and len(tbl.findall(q("tr"))) > 8:
warns.append("长表格缺少表头重复 tblHeader")
if body.find(q("sectPr")) is None and len(body.findall(".//" + q("sectPr"))) == 0:
errors.append("缺少节属性 sectPr")
print("ERRORS:", errors or "无")
print("WARNINGS:", warns or "无")
sys.exit(1 if errors else 0)
发现 ❌ 必须修复后重新生成。
边界
- 不创建
.doc(旧格式)——需要时用 LibreOffice 转换;不支持加密文档。 - 用户给了大纲/模板时严格遵循,不擅自增删重排;缺关键信息用【】占位并向用户说明。
- 修订(track changes)与复杂批注需直接操作 OOXML,改动前先备份原文件。
- 生成代码含中文时优先把脚本写成 UTF-8 文件再运行,避免 heredoc 编码问题。
