OfficeCLI 深入实战:用命令行生成 Word 多级编号、项目符号与独立计数器(abstractNum / num / numPr 全解析)
OfficeCLI 深入实战用命令行生成 Word 多级编号、项目符号与独立计数器abstractNum / num / numPr 全解析【免费下载链接】OfficeCLIOfficeCLI 是首款也是最佳的专为 AI 代理设计的命令行工具可用于读取、编辑和自动化处理 Word、Excel 和 PowerPoint 文件。它免费、开源仅包含一个二进制文件无需安装 Office 套件。项目地址: https://gitcode.com/iOfficeAI/OfficeCLI导读本文以仓库内 examples/word/numbering.md 演示文档为主线完整讲解 OfficeCLI 的 docx 编号Numbering命令行 API如何用abstractNum定义编号模板、用num创建编号实例、用段落的numPrnumIdilvl把编号应用到正文以及如何实现独立计数器、startOverride重启、样式承载编号和创建后修改等进阶能力。读完本文你将能只用一条条officecli add / set / query命令从零构建出 Word 打开即渲染正确的多级列表与自定义项目符号并理解这些命令背后写入的 OOXML 结构。编号体系三件套abstractNum、num、numPr在深入命令之前先建立 OOXML 编号的模型。Word 的numbering.xml部件里编号由三层对象协作完成abstractNum抽象编号模板定义列表长什么样——最多 9 个级别ilvl0..8每个级别有独立的编号格式numFmt、标记文本lvlText、缩进、起始值和标记字符样式。它是可复用的模板多个num可以共享同一个abstractNum。num编号实例一个薄指针通过abstractNumId引用某个abstractNum。段落引用的是numId即该实例的w:numId而不是abstractNumId。计数器状态存在于num这一层因此同一模板可以派生出多个互不干扰的实例。段落numPr段落的pPr中的w:numPr包含w:numId引用哪个num和w:ilvl使用第几级。它也可以放在段落样式中让所有应用该样式的段落自动继承编号。这三者的层级关系在仓库 schema 中有机器可读的定义schemas/help/docx/abstractNum.json、schemas/help/docx/num.json、schemas/help/docx/level.json以及 schemas/help/docx/paragraph.json段落侧numId/ilvl。演示文档对应的三份配套文件examples/word/numbering.sh完整构建脚本341 行、约 60 条命令覆盖全部特性examples/word/numbering.docx生成产物包含 8 个章节、5 个不同的abstractNum定义examples/word/numbering.md本文所依据的演示说明。复现演示一条命令构建整份文档先确认officecli可执行然后在examples/word目录下运行cd examples/word bash numbering.sh # → numbering.docx脚本内部的核心流程是officecli create新建空白文档 → 数十条officecli add追加标题、编号定义与段落 →officecli close收尾 →officecli validate校验文档完整性。值得注意的是脚本刻意没有加set -e脚本头部注释解释了原因——为了向前兼容当遇到UNSUPPORTED props警告officecli 此时退出码为 2时脚本继续执行保证整份演示文档仍然完整产出。这在批量编排 Agent 任务时是个实用技巧对旧版本工具报出的未知属性不必让整个构建中断。第一节三级自定义编号列表abstractNum num numPr 全链路这是最核心的一节先创建一个id100的abstractNum再用num引用它最后让三个段落分别挂在第 0/1/2 级上# 创建 abstractNum 定义id100 officecli add numbering.docx /numbering --type abstractNum \ --prop id100 \ --prop nameShowcaseMultilevel \ --prop typehybridMultilevel \ --prop level0.formatdecimal --prop level0.text%1. \ --prop level0.indent720 --prop level0.hanging360 \ --prop level0.justificationleft --prop level0.sufftab \ --prop level0.colorC00000 --prop level0.boldtrue --prop level0.size14 \ --prop level1.formatlowerLetter --prop level1.text%2) \ --prop level1.indent1440 --prop level1.hanging360 \ --prop level1.color2E74B5 --prop level1.italictrue \ --prop level2.formatlowerRoman --prop level2.text%3. \ --prop level2.indent2160 --prop level2.hanging360 \ --prop level2.color666666 # 创建指向 abstractNum #100 的 num 实例并捕获自动分配的 id NUMID_A$(officecli add numbering.docx /numbering --type num --prop abstractNumId100 \ | sed -n s|.*id\([0-9]*\)\].*|\1|p) # 挂到不同缩进级别的段落 officecli add numbering.docx /body --type paragraph \ --prop textProject Phoenix kickoff agenda \ --prop numId$NUMID_A --prop ilvl0 officecli add numbering.docx /body --type paragraph \ --prop textStakeholder alignment \ --prop numId$NUMID_A --prop ilvl1 officecli add numbering.docx /body --type paragraph \ --prop textidentify decision makers \ --prop numId$NUMID_A --prop ilvl2生成结果在 Word 中打开为1. Project Phoenix kickoff agenda a) Stakeholder alignment i. identify decision makers参数语义速查参数说明idabstractNum标识符w:abstractNumId在/numbering内唯一省略则自动分配name显示在 Word定义新编号格式对话框中的名称w:nametypemultiLevelTypehybridMultilevelWord 临时列表常用默认/multilevel/singleLevel锁死 0 级levelN.formatnumFmtdecimal/lowerLetter/upperLetter/lowerRoman/upperRoman/bullet/decimalZero等levelN.textlvlText%N插入第 N 级计数器如%1.、%2)非计数器格式则写死字面量levelN.indent左缩进缇 twips1 英寸 1440 缇levelN.hanging悬挂缩进缇levelN.justificationlvlJcleft/center/rightlevelN.suff编号与正文之间的分隔符tab默认/space/nothinglevelN.color/bold/italic/size标记字符的 run 属性rPrsize裸数字按 pt 解析abstractNumIdnum→abstractNum的引用仅--type num使用ilvl段落使用的级别0 基与 OOXML 的numLevel互为别名源码印证模板是如何落盘的add ... --type abstractNum与add ... --type num在 WordHandler.Add.Structure.cs 中分别由AddAbstractNumL2222与AddNumL2061实现二者最终汇入BuildAbstractNumElementL2257。该函数严格按照 ECMA-376 的子元素顺序构造w:abstractNumnsid → multiLevelType → tmpl → name → styleLink → numStyleLink → lvl[0..8]每个levelN子元素的构造顺序也严格对应 schemastart → numFmt → suff → lvlText → lvlJc → pPr/ind → rPr其中indent未显式指定时默认(lvl1)*720、hanging默认360、lvlText默认%{lvl1}.有序或•无序未显式设置的级别会自动按decimal / lowerLetter / lowerRoman循环补齐——这就是为什么只配了 3 级却能在 Word 中安全用到第 8 级。另外BuildAbstractNumElement还做了别名归一化fmt/numFmt→format、lvlText→text含levelN.*形式保证get读出的规范属性名能直接喂回add而不触发UNSUPPORTED props告警实现完整的 dump→批量重放闭环。在AddNum中还有一道防呆校验abstractNumId指向的模板必须已存在于/numbering否则直接抛错——因为 Word 遇到悬空的numId会静默丢弃编号这种失败模式极难排查仓库选择在写入时拦截L2097-L2105。第二节独立计数器 vs. 继续计数continue同一个abstractNum上可以挂多个num实例。默认情况下每个新实例都会自动注入startOverride.01因此计数器各自从 1 开始、互不影响如果希望严格复刻 Word 的继续上一个num的计数则传--prop continuetrue# 独立计数器默认行为自动注入 startOverride NUMID_B$(officecli add numbering.docx /numbering --type num \ --prop abstractNumId100 \ | sed -n s|.*id\([0-9]*\)\].*|\1|p) # Word 式延续计数不注入 startOverride NUMID_CONT$(officecli add numbering.docx /numbering --type num \ --prop abstractNumId100 --prop continuetrue \ | sed -n s|.*id\([0-9]*\)\].*|\1|p) officecli add numbering.docx /body --type paragraph \ --prop textList B starts fresh at 1 (default behavior) \ --prop numId$NUMID_B --prop ilvl0 officecli add numbering.docx /body --type paragraph \ --prop textList C continues from List As count (continuetrue) \ --prop numId$NUMID_CONT --prop ilvl0这段行为的源码在AddNum的 L2171-L2193默认注入的起始值取自abstractNum第 0 级的start通常为 1当continue为真时跳过注入w:num就退化为 Word 原生的共享计数器语义。这一默认值的选择是刻意的产品决策——新的num实例 独立计数器更符合 API 使用者的直觉而continuetrue为需要 Word 字面行为的场景保留出口。相关参数定义见 schemas/help/docx/num.json 的start与continue条目。第三节用 startOverride 从任意数字重启编号给num传--prop startN会生成w:lvlOverride/w:startOverride第 0 级让列表可以从任意数字开始NUMID_C$(officecli add numbering.docx /numbering --type num \ --prop abstractNumId100 --prop start100 \ | sed -n s|.*id\([0-9]*\)\].*|\1|p) officecli add numbering.docx /body --type paragraph \ --prop textNumbered starting from 100 \ --prop numId$NUMID_C --prop ilvl0 officecli add numbering.docx /body --type paragraph \ --prop textContinues from 101 \ --prop numId$NUMID_C --prop ilvl0输出100. Numbered starting from 100→101. Continues from 101。源码中start只是startOverride.0的简写L2144-L2145更细粒度地可以用startOverride.NN 0..8分别重置任意级别的计数器且越界级别N0 或 N8会被显式拒绝L2161-L2169因为 Word 的级别只有 0..8。注意区分两个startnum上的start注入的是实例级lvlOverride.startOverride而abstractNum levelN.start是模板级的默认起始值二者作用层不同。第四节自定义项目符号列表Unicode 字形 逐级样式formatbullet时levelN.text不再解释%N占位符而是直接作为字形输出levelN.font用于指定包含该字形的字体如 Wingdings/Symbol/Arialofficecli add numbering.docx /numbering --type abstractNum \ --prop id200 --prop nameStarBullet --prop typehybridMultilevel \ --prop level0.formatbullet --prop level0.text★ \ --prop level0.colorE8B003 --prop level0.size12 \ --prop level1.formatbullet --prop level1.text▶ \ --prop level1.fontArial \ --prop level1.color2E74B5 --prop level1.indent1440 \ --prop level2.formatbullet --prop level2.text● \ --prop level2.color70AD47 --prop level2.indent2160 NUMID_BULLET$(officecli add numbering.docx /numbering --type num \ --prop abstractNumId200 \ | sed -n s|.*id\([0-9]*\)\].*|\1|p) officecli add numbering.docx /body --type paragraph \ --prop textTop-level milestone \ --prop numId$NUMID_BULLET --prop ilvl0 officecli add numbering.docx /body --type paragraph \ --prop textSub-milestone with deliverable \ --prop numId$NUMID_BULLET --prop ilvl1 officecli add numbering.docx /body --type paragraph \ --prop textNitty-gritty detail \ --prop numId$NUMID_BULLET --prop ilvl2BuildAbstractNumElement中formatbullet或unordered/ul别名会让未显式指定的级别自动填入• / ◦ / ▪循环字形L2305-L2306、L2359-L2360。标记字符的rPr只有在至少提供一个font/size/color/bold/italic时才写出避免产生空的w:rPr/元素L2392-L2422。第五节Mode A——num自动创建abstractNum以上各节都是先建模板、再建实例的 Mode B。如果add --type num时直接给levelN.*属性而不给abstractNumId处理器会现场生成一个匹配的abstractNum并把新num链接过去这就是 Mode ANUMID_AUTO$(officecli add numbering.docx /numbering --type num \ --prop level0.formatupperRoman --prop level0.text%1. \ --prop level0.indent720 --prop level0.size12 \ --prop level0.color7030A0 --prop level0.boldtrue \ | sed -n s|.*id\([0-9]*\)\].*|\1|p) officecli add numbering.docx /body --type paragraph \ --prop textThe first part of the proposal \ --prop numId$NUMID_AUTO --prop ilvl0AddNum的开头L2069-L2112用hasAbsId/hasFormat区分三种模式并做互斥校验同时给出abstractNumId和format/text/indent/type等模式 A 属性会直接抛错两者都没有也会抛错并给出提示。模式 A 下新abstractNum的 id 取当前最大值 1命令的输出路径会带上新分配的两个 id/numbering/abstractNum[id…]与/numbering/num[id…]因此sed提取id的惯用法可以继续复用。三种模式速览Mode AnumlevelN.*无abstractNumId→ 自动建模板Mode BnumabstractNumIdN→ 复用已有模板最常见Mode CnumabstractNumIdNstart/startOverride.N→ 复用模板并注入级别重启。第六节样式承载编号style-borne numPr编号引用也可以放在段落样式里样式持有numPr段落只要应用该样式即可继承编号段落自身无需携带numId# 为样式准备专属的 abstractNum num officecli add numbering.docx /numbering --type abstractNum \ --prop id300 --prop nameStyleBorne \ --prop level0.formatdecimalZero --prop level0.text%1. \ --prop level0.indent720 --prop level0.colorC00000 NUMID_STYLE$(officecli add numbering.docx /numbering --type num \ --prop abstractNumId300 \ | sed -n s|.*id\([0-9]*\)\].*|\1|p) # 段落样式持有 numPr officecli add numbering.docx /styles --type style \ --prop idShowcaseListItem \ --prop nameShowcase List Item \ --prop typeparagraph --prop basedOnNormal \ --prop numId$NUMID_STYLE --prop ilvl0 # 段落只引用样式不写 numId officecli add numbering.docx /body --type paragraph \ --prop textInherits numbering through style \ --prop styleShowcaseListItem这里的样式创建走add /styles --type style实现见 WordHandler.Add.Structure.cs 中AddStyle相关逻辑numId/ilvl会被写入样式的w:pPr/w:numPr。这种样式即接口的模式特别适合文档体系化修改一处样式所有引用段落的编号外观同步更新同时段落正文保持干净无numPr。第七节创建后修改 abstractNumset 命令set命令可以直接命中已存在的级别并改写其属性实现事后修订# 把 #100 的第 3 级改成新格式、新标签、新颜色与字号 officecli set numbering.docx /numbering/abstractNum[id100]/level[3] \ --prop formatdecimal --prop textStep %4 ⇒ \ --prop color70AD47 --prop boldtrue --prop size12 # 新建一个 num 实例来验证修改后的级别 NUMID_DEEP$(officecli add numbering.docx /numbering --type num \ --prop abstractNumId100 \ | sed -n s|.*id\([0-9]*\)\].*|\1|p) officecli add numbering.docx /body --type paragraph \ --prop textDeepest step (modified after creation) \ --prop numId$NUMID_DEEP --prop ilvl3注意路径写法同时兼容/level[3]位置形式与/lvl[ilvl3]两种语法。在 WordHandler.Set.cs 中/numbering/abstractNum[idN]及其level[L]子路径会在通用路径解析之前被正则拦截L479-L480路由到SetAbstractNumPath专门处理确保set不会误入通用元素替换分支。这正是文档中Set-only级别标志direction/isLgl/lvlRestart能落地的入口见下一节。第八节其余特性——styleLink、numStyleLink、level.start、direction、isLgl、lvlRestart本节补齐abstractNum的 Add-only 键与 level 的 Set-only 标志officecli add numbering.docx /numbering --type abstractNum \ --prop id400 --prop nameCoverageAbs --prop typemultilevel \ --prop styleLinkCoverageStyle \ --prop numStyleLinkOutlineRef \ --prop level0.formatdecimal --prop level0.text%1. --prop level0.start1 \ --prop level1.formatlowerLetter --prop level1.text%2) --prop level1.start3 \ --prop level2.formatlowerRoman --prop level2.text%3. --prop level2.start5 # Set-only 标志direction、isLgl、lvlRestart officecli set numbering.docx /numbering/abstractNum[id400]/level[1] \ --prop directionrtl officecli set numbering.docx /numbering/abstractNum[id400]/level[2] \ --prop isLgltrue --prop lvlRestart0各参数含义详见 schemas/help/docx/abstractNum.json 与 schemas/help/docx/level.jsonstyleLink反向引用编号样式名写入w:styleLinkAdd 时设置numStyleLink通过编号样式链接到另一个abstractNum写入w:numStyleLinkAdd 时设置levelN.start模板级每级起始计数器区别于num.start的实例级startOverridedirectionrtl在级别pPr上写入w:bidi/用于阿拉伯语/希伯来语等 RTL 列表ltr是规范的清除写法不写元素direction/dir/bidi三个键等价isLgl无论numFmt是什么计数器一律按十进制渲染——法律文书式编号legal numbering的典型需求lvlRestart设置该计数器在哪个级别自动重启0 永不自动重启。源码中AddLvl对lvlRestart整数、Int32 校验、isLgl布尔、directionrtl/ltr 双值枚举以及suff/justification/font/size/color/bold/italic/underline都有显式解析与校验L2487-L2634。需要注意AddLvl使用AppendChild而非 schema 感知的AddChild追加级别——因为后者会把w:lvl当作单实例子元素槽位静默替换已存在的同级级别显式AppendChild才能让 0..8 各级别共存L2636-L2649。编号格式numFmt的完整取值面levelN.format由 WordHandler.Set.cs 的ParseNumberFormatL1537统一解析除常见值外还覆盖了大量脚本化本地化格式。常用值decimal, decimalZero, upperRoman, lowerRoman, upperLetter, lowerLetter, ordinal, cardinalText, ordinalText, bullet, hex, numberInDash, chicago脚本化/本地化格式ECMA-376 §17.18.59 ST_NumberFormat 的完整子集arabicAlpha, arabicAbjad, hebrew1, hebrew2, bahtText, dollarText, hindiNumbers, hindiVowels, hindiCounting, hindiConsonants, thaiCounting, thaiNumbers, thaiLetters, chineseCounting, chineseCountingThousand, chineseLegalSimplified, japaneseCounting, japaneseLegal, japaneseDigitalTen(Thousand), koreanCounting, koreanLegal, koreanDigital(2), ideographDigital, ideographTraditional, ideographZodiac( Traditional), ideographEnclosedCircle, ideographLegalTraditional, taiwaneseCounting(Thousand), taiwaneseDigital, vietnameseCounting, russianLower, russianUpper, iroha(FullWidth), ganada, aiueo(FullWidth), chosung, decimalFullWidth(2), decimalHalfWidth, decimalEnclosedCircle(Chinese), decimalEnclosedFullstop, decimalEnclosedParen这正是get读回w:numFmt后能原样写回add/set的往返round-trip保证也是officecli dump导出其他文档编号定义后可在新文档批量重放的前提相关兼容逻辑见 BatchCompat.cs 的RemapNumberingIds。特性覆盖总表FeatureSectionabstractNumwithid,name,type1, 4, 5, 6, 7, 8levelN.format(decimal/lowerLetter/lowerRoman/upperRoman/bullet/decimalZero)1, 4, 5, 6, 8levelN.text(%Ncounter substitution Unicode glyphs)1, 4, 5, 8levelN.indent,levelN.hanging,levelN.justification,levelN.suff1levelN.color,levelN.bold,levelN.italic,levelN.size,levelN.font1, 4, 5levelN.start(per-level starting counter)8numwithabstractNumId(Mode B)1, 2, 3, 6, 7, 8numwithlevelN.*only (Mode A — auto-creates abstractNum)5num start(injectslvlOverride.startOverride)3continuetrue(skip startOverride injection)2Independent counters (multiplenumon sameabstractNum)2Style-bornenumPrvia/styles --type style6seton/numbering/abstractNum[idN]/level[L]7, 8styleLink,numStyleLink8directionrtl,isLgl,lvlRestart(Set-only level flags)8校验与检查生成的文档脚本末尾用validate做了完整度校验你也可以随时用只读命令回读文档内部的编号结构验证每一步写入是否符合预期# 列出全部编号定义含 abstractNum 与 num 的计数 officecli query numbering.docx /numbering # 查看某个 abstractNum 的完整内容 officecli get numbering.docx /numbering/abstractNum[id100] # 查看其中某个级别 officecli get numbering.docx /numbering/abstractNum[id100]/level[1] # 列出所有引用编号列表的段落 officecli query numbering.docx paragraphquery /numbering在 WordHandler.Query.cs 中会返回abstractNumCount与numCount两个计数L1953-L1960get则会展开type/name/styleLink/numStyleLink及各级level[L]子节点L385-L401其输出键与add的输入键一一对应可直接作为下一轮批量操作的输入——这让生成 → 校验 → 复用定义形成闭环。结语至此OfficeCLI 的 docx 编号能力已经完整覆盖从abstractNum模板9 级、逐级格式与样式到num实例Mode A/B/C、独立计数器、startOverride重启再到段落与样式的numPr引用以及创建后的set修订。整套 API 的语义与 OOXML schema 严格对齐且都沉淀在 schemas/help/docx/ 的机器可读定义中。无论是让 AI Agent 自动生成结构化条款文档、还是脚本化批量编排多级大纲这些命令都可以直接嵌入你的工作流——配合validate与query做回归检查即可获得与 Word 原生编辑等价的可靠产出。【免费下载链接】OfficeCLIOfficeCLI 是首款也是最佳的专为 AI 代理设计的命令行工具可用于读取、编辑和自动化处理 Word、Excel 和 PowerPoint 文件。它免费、开源仅包含一个二进制文件无需安装 Office 套件。项目地址: https://gitcode.com/iOfficeAI/OfficeCLI创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考