关于 officecli-word-form
officecli-word-form 是用于创建结构化可填写 Word 表单和合并模板的 OfficeCLI 技能。它侧重表单控件、字段保护和数据验证,而非普通文档排版。
主要功能
- 01结构化内容控件
- 02下拉列表与日期字段
- 03旧式复选框字段
- 04邮件合并字段
- 05表单保护与质检
适用场景
- 01创建受保护的信息收集表
- 02制作含下拉列表和日期字段的模板
- 03准备邮件合并文档
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ iofficeai/ officecli --skill officecli-word-form安装后在 Claude Code 中输入/officecli-word-form确认已加载。
详细介绍
OfficeCLI Word 表单 Skill
本 Skill 独立于 docx,不是 docx 的场景层。 表单的载荷(<w:sdt> 内容控件、<w:ffData> 旧式表单域、<w:fldChar> 邮件合并、documentProtection)与 docx 的段落/标题/样式基本元素属于不同的元素类别。其质量检查也不同:docx 的交付检查关注视觉版式和实时 PAGE 域,本 Skill 的检查关注数据链路(保护是否生效 / 别名+标签 / 选项是否注入 / 名称 ≤ 20 字符 / 无下划线反模式)。反向转交: 如果用户文档没有可填写字段(报告、信函、备忘录、论文、提案),请转交 officecli-docx 或某个 docx 场景 Skill,不要使用本 Skill。
开始之前(关键)
如果未安装 officecli:
macOS / Linux
if ! command -v officecli >/dev/null 2>&1; then
curl -fsSL https://d.officecli.ai/install.sh | bash
fi
Windows (PowerShell)
if (-not (Get-Command officecli -ErrorAction SilentlyContinue)) {
irm https://d.officecli.ai/install.ps1 | iex
}
验证:officecli --version
如果首次安装后仍找不到 officecli,请打开新的终端并重新运行验证命令。
如果上述安装命令失败(例如被安全策略拦截、无网络访问或权限不足),请手动安装:从 https://github.com/iOfficeAI/OfficeCLI/releases 下载适用于你所用平台的二进制文件,然后重新运行验证命令。
先查帮助规则
本 Skill 讲解真实表单需要什么,而不是每一个命令行参数。当某个属性 / 别名 / 枚举值不确定时,请先查阅帮助再猜测:officecli help docx [element] [--json](例如 sdt、formfield、field)。帮助信息与已安装的 CLI 版本绑定,具有权威性——如果本 Skill 与帮助内容不一致,以帮助为准(sdt 的属性集合随时间不断增加,请以 help docx sdt 为准,而不是硬编码的列表)。
心智模型与继承关系
Word 表单本质上是一个 .docx,外加四层普通 docx Skill 不涉及的 OpenXML 载荷:<w:sdt> 内容控件(类型:文本 / 富文本 / 下拉列表 / 组合框 / 日期 / 图片 / 组)、<w:ffData> 旧式 FormField(仍是获得真正复选框的唯一方式——SDT 的 type=checkbox 尚未实现)、<w:fldChar> 复杂域(MERGEFIELD、REF、PAGEREF、SEQ、IF——属于模板阶段,而非用户填写阶段),以及 documentProtection(使非域文本在 Word 中只读的锁定机制;在 CLI 中,protection=forms 会锁定非域内容的编辑(需要 --force 或 raw-set),但仍允许编辑表单域(SDT),这正是表单保护的意义所在)。
不继承 docx v2 的规则。 docx 的交付检查(封面填充率、实时 PAGE 检查)不适用于表单——表单质量检查使用 view forms + query sdt alias+tag + protectionEnforced。
反向转交至 docx。 对于报告 / 信函 / 备忘录 / 论文 / 演示提案 / 任何没有可编辑字段的文档,请转交回 officecli-docx。只有当文档的用途是数据采集或模板合并时,才使用本 Skill。
Shell 与执行规范
一次只执行一条命令,读取输出后再执行下一条。 OfficeCLI 是增量式的,每次 add / set / remove 都会立即修改文件。以下所有示例均使用 shell 变量 FILE=form.docx。
三层 shell 转义规则:
- 含
[N]的路径必须加引号 —— zsh/bash 会对方括号进行通配符展开。officecli get "$FILE" /body/sdt[1]会报错no matches found。正确写法:officecli get "$FILE" '/body/sdt[1]'。 - 含
$的属性值必须用单引号 ——"Total: $50,000"在$50被变量展开后会变成"Total: ,000"。正确写法:'Total: $50,000'。 --after find:<text>外层使用单引号,不要用内层双引号 ——--after find:"Client Signature:"会使引号成为搜索字符串的一部分,导致匹配失败。正确写法:--after 'find:Client Signature:'。
WARNING: UNSUPPORTED(退出码 2)表示该元素被静默写错。 CLI 会在不包含被拒绝属性的情况下创建该元素(退出码 2 表示带注意事项运行;只有当所有属性都被拒绝时,--json 才会报告 success: false)。构建日志中出现任何 UNSUPPORTED,都说明当前 CLI 不接受该元素上的某个属性名——应立即停止,通过 help docx <element> 查询正确的属性名(大多数 SDT 属性如 items/format/lock 现已支持;maxlength 不支持),修正命令后重新执行。不要在此基础上继续操作。
protection=forms 必须是最后一个结构性命令。 设置后,protection=forms 会锁定非字段内容(这些编辑需要 --force 或 raw-set),但仍允许编辑表单字段(SDT)——这正是表单保护的意义所在。编辑非字段内容(例如 set /body/p[N] --prop text=)会被拒绝(ERROR: Document is protected … use --force);编辑表单字段(例如 set /body/sdt[N] --prop text= / --prop alias=)则会成功(退出码 0)。可使用 Query("editable") 查找可填写的字段。因此应先完成所有非字段(静态版式)编辑,再加锁;如果之后必须编辑静态内容,需添加 --force(或临时解除保护、编辑后重新加锁)。
--after find: 操作要点
--after find:<text> 匹配的是第一个出现的位置。锚点不准确会导致插入位置错误,且排查成本高。遵循以下三条规则:
- 锚点必须在全文中唯一。 在中英双语合同中,"甲方签字"会同时匹配双方——应使用唯一短语,例如 "甲方签字(Service Provider)" 或完整的英文标题。
- 插入之后,
/body/p[last()]不可靠——find 插入会改变<w:body>的子元素顺序。要继续操作新段落,应读取其真实的 paraId:officecli query "$FILE" paragraph --json | jq -r '.data.results[-1].format.paraId'。 - 中文及全角括号
()在find中按字面匹配,但若不确定,应先执行officecli view "$FILE" text | grep -n "锚点",确认文件中的确切字节。
# Trap: first-match hits 甲方 only, 乙方 missed
officecli add "$FILE" /body --type sdt --after 'find:签字'
# Fix: two signatories, two unique anchors
officecli add "$FILE" /body --type sdt --prop alias=Party_A_Name --prop tag=party_a \
--after 'find:甲方签字(Service Provider)'
PID_A=$(officecli query "$FILE" paragraph --json | jq -r '.data.results[-1].format.paraId')
officecli add "$FILE" "/body/p[@paraId='$PID_A']" --type sdt --prop alias=Party_A_Title --prop tag=party_a_title
通过 --after find: 插入的内联 SDT 会作为匹配段落的子元素添加,而不是新建段落——当标签与 SDT 必须位于同一行时使用此方法。
什么才算真正的表单(标识)
真正可填写的表单需要结构化字段 + 文档保护。
| 方案 | Word 用户看到的内容 | 命令行可读 | 真正的表单? |
|---|---|---|---|
SDT 控件 + protection=forms | 灰色边框字段;其余部分锁定 | query sdt / view forms | 是 |
FormField 复选框 + protection=forms | 真正可点击的复选框;其余部分锁定 | query formfield / view forms | 是(仅复选框) |
| MERGEFIELD 占位符 | 由下游引擎合并后显示为 «CustomerName» | query field | 是(模板阶段) |
下划线 ___ / 空行 | 仅视觉效果;整个文档可编辑 | 否,没有结构化字段 | 否 |
不要用下划线模拟字段。 姓名:_______________ 不会产生任何结构化数据,并且会绕过所有验证。始终使用 --type sdt 或 --type formfield。
复选框使用 formfield,而不是 SDT。 --type sdt --prop type=checkbox 会以退出码 1 失败(SDT type 'checkbox' is not implemented)。所有方案中的复选框都使用 --type formfield --prop type=checkbox。
MERGEFIELD 是独立的一条路线。 view forms 只列出 SDT 和 formfield;query field 只列出复杂字段。两者是互不相交的两套清单,可以同时存在于同一文件中。
输出要求(硬性底线)
每个表单都必须满足以下要求;交付检查(Delivery Gate)会将每一项作为可执行检查来验证。
- 已强制启用
protection=forms(get $FILE /→protectionEnforced=True)。 - 每个 SDT 同时具有
alias和tag。 - 每个下拉列表/组合框在
view forms中都有非空的items=...。 - 每个日期 SDT 都显示预期的
format=...。 - 每个锁定的 SDT 按预期显示
lock=sdtLocked/contentLocked/sdtContentLocked。 - 构建日志中不得出现
WARNING: UNSUPPORTED。 - 任何 SDT 上都不得出现
type=checkbox。 - 每个 formfield 的
name不超过 20 个字符。 - 不得存在下划线线条或空行占位符。
- 字段类型与用户意图一致(短文本 / 段落 / 固定列表 / 列表+自定义 / 日期 / 布尔值)。
三种路径(核心决策)
SDT 属性是 add 命令的一等属性——请使用 officecli help docx sdt 确认当前支持的属性集。目前包括 type, tag, alias, text, items, format, lock, placeholder/placeholderText, date.*,以及 SDT 类型 text / richtext / dropdown / combobox / date / group / picture。因此大多数表单只需纯 add,无需 raw-set,也无需 Word 模板。仅剩两种路径适用于确实无法通过上述方式实现的情况。在编写任何命令之前先选定路径。
路径 A——纯命令行(几乎所有情况的默认选择)
适用于:任何 text / richtext / dropdown / combobox / date / picture / group SDT,包括其选项、日期格式和锁定设置。直接在 add 上传递这些属性;使用 get '/body/sdt[N]' --json 逐一验证其是否已保存。
# dropdown WITH its options and a non-default date format, in one add each — no raw-set:
officecli add "$FILE" /body --type sdt \
--prop type=dropdown --prop alias="Department" --prop tag=dept \
--prop items="Engineering,Finance,HR"
officecli add "$FILE" /body --type sdt \
--prop type=date --prop alias="Start Date" --prop tag=start \
--prop format="yyyy年MM月dd日"
# lock works on add AND set:
officecli add "$FILE" /body --type sdt --prop type=text --prop tag=full_name \
--prop alias="Full Name" --prop text="Enter full name" --prop lock=sdtLocked
# protection comes last, once all fields exist:
# officecli set "$FILE" / --prop protection=forms
路径 B——命令行 + raw-set 桥接(仅适用于帮助信息未暴露的属性)
适用于:需要某个 help docx sdt 未列出的 SDT 属性时(例如 w:value 必须与显示文本不同的 <w:listItem>,或没有对应 --prop 的 sdtPr 子元素)。raw-set 是 OfficeCLI 通用的 OpenXML 回退命令,officecli --help 将其列为顶级命令。普通下拉列表应使用 --prop items=(路径 A);此处的 raw-set 是例外,而非常规做法。
# Display text ≠ stored value — not expressible via items=, so inject the listItems directly:
officecli add "$FILE" /body --type sdt --prop type=dropdown --prop alias="Department" --prop tag=dept
officecli raw-set "$FILE" /document \
--xpath "//w:sdt[w:sdtPr/w:tag/@w:val='dept']/w:sdtPr/w:dropDownList" \
--action append \
--xml '<w:listItem xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:displayText="Engineering" w:value="ENG"/><w:listItem xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:displayText="Finance" w:value="FIN"/>'
路径 C——Word 模板(仅用于其他任何 API 都无法实现的情况)
适用于:真正的 SDT 复选框(type=checkbox 仍会以退出码 1 失败,请改用旧式 FormField,参见 §旧式 FormField)、placeholderDocPart 提示文本部件,或超出 --prop 能力范围的自定义 richtext 外观 / 跨部件嵌套。图片和分组 SDT 已不再属于此路径,可通过路径 A 正常添加。
# One-time in Word: Developer tab → Insert Content Control → Save as template.docx
cp templates/onboarding_with_signature.docx "$FILE"
officecli open "$FILE"
officecli view "$FILE" forms # inspect embedded controls + paths
officecli set "$FILE" '/body/sdt[@sdtId=3]' --prop text="Jane Smith"
officecli set "$FILE" / --prop protection=forms
决策表
| 需求 | 路径 | 说明 |
|---|---|---|
| 带默认字符串的文本 / 富文本 SDT | A | --prop type/alias/tag/text |
| 必须锁定的文本 SDT | A | --prop lock=sdtLocked 可用于 add(以及 set) |
| 带选项的下拉列表 / 组合框 | A | --prop items="A,B,C" |
| 使用非默认格式的日期 SDT | A | --prop format="yyyy年MM月dd日" |
| 签名图片 SDT、分组 SDT | A | 直接使用 --prop type=picture / type=group 添加 |
| 存储值与显示文本不同的下拉列表 | B | 通过 raw-set 追加 <w:listItem w:value=…> |
| 真正的复选框 | FormField | --type formfield --prop type=checkbox(参见 §Legacy FormField) |
| 邮件合并占位符 | MERGEFIELD | --type field --prop fieldType=mergefield(参见 §MERGEFIELD) |
| 真正的 SDT 复选框 / 占位符部件 / 自定义外观 | C | 先在 Word 中构建骨架,再通过 CLI 填充 |
快速开始 — 路径 A + FormField(最小化填写表单)
两个 SDT 文本字段、一个复选框、保护。可直接粘贴并按需调整;这是值得交付的最小表单。
FILE=intake.docx
officecli close "$FILE" 2>/dev/null; rm -f "$FILE" # preflight: clear stale resident / prior file (cold-start after CLI upgrade commonly leaks a resident)
officecli create "$FILE"
officecli open "$FILE"
officecli set "$FILE" / --prop title="Employee Onboarding Intake" \
--prop docDefaults.font="Calibri" --prop docDefaults.fontSize="12pt"
officecli add "$FILE" /body --type paragraph \
--prop text="Employee Onboarding Intake" --prop style=Heading1 \
--prop size=20 --prop bold=true --prop spaceAfter=18pt
officecli add "$FILE" /body --type paragraph \
--prop text="Full Name:" --prop size=11 --prop bold=true --prop spaceAfter=4pt
officecli add "$FILE" /body --type sdt --prop type=text \
--prop alias="Full Name" --prop tag=full_name --prop text="Enter full name"
officecli add "$FILE" /body --type paragraph \
--prop text="Start Date:" --prop size=11 --prop bold=true --prop spaceAfter=4pt
officecli add "$FILE" /body --type sdt --prop type=date \
--prop alias="Start Date" --prop tag=start_date
officecli add "$FILE" /body --type paragraph \
--prop text="Read and agree to employee handbook" --prop size=11 --prop spaceAfter=4pt
officecli add "$FILE" /body --type formfield \
--prop type=checkbox --prop name=agree_handbook --prop checked=false
officecli set "$FILE" '/body/sdt[1]' --prop lock=sdtlocked
officecli set "$FILE" '/body/sdt[2]' --prop lock=sdtlocked
officecli set "$FILE" / --prop protection=forms
officecli close "$FILE"
officecli view "$FILE" forms
路径 B — raw-set 方案
三个方案几乎可以覆盖 SDT 表单中所有复杂属性需求。
B1 — 下拉列表选项(追加)
# Skeleton (Path A)
officecli add "$FILE" /body --type sdt --prop type=dropdown \
--prop alias="Department" --prop tag=dept
# Inject items
officecli raw-set "$FILE" /document \
--xpath "//w:sdt[w:sdtPr/w:tag/@w:val='dept']/w:sdtPr/w:dropDownList" \
--action append \
--xml '<w:listItem xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:displayText="Engineering" w:value="Engineering"/><w:listItem xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:displayText="Finance" w:value="Finance"/><w:listItem xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:displayText="HR" w:value="HR"/>'
# Verify
officecli get "$FILE" '/body/sdt[1]' # expect: type=dropdown items=Engineering,Finance,HR
模板。 仅需替换 <TAG> / <LABEL> / <VALUE>。每个根级 <w:listItem> 都必须包含 xmlns:w=...,因为 raw-set 不会继承命名空间前缀。可在一次调用中串联多个 <w:listItem>,选项顺序会被保留。
B2 — 组合框选项(与 B1 相同,XPath 结尾不同)
officecli add "$FILE" /body --type sdt --prop type=combobox \
--prop alias="Current Medication" --prop tag=current_med
officecli raw-set "$FILE" /document \
--xpath "//w:sdt[w:sdtPr/w:tag/@w:val='current_med']/w:sdtPr/w:comboBox" \
--action append \
--xml '<w:listItem xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:displayText="Antihypertensives" w:value="Antihypertensives"/><w:listItem xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:displayText="Insulin" w:value="Insulin"/><w:listItem xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" w:displayText="Other (specify)" w:value="Other"/>'
与 B1 的唯一区别:XPath 结尾使用 w:comboBox 而非 w:dropDownList。组合框允许用户输入自定义内容;下拉列表则不允许。
B3 — 日期格式(setattr)
officecli add "$FILE" /body --type sdt --prop type=date \
--prop alias="Contract Start Date" --prop tag=contract_start
# Chinese: yyyy年MM月dd日
officecli raw-set "$FILE" /document \
--xpath "//w:sdt[w:sdtPr/w:tag/@w:val='contract_start']/w:sdtPr/w:date/w:dateFormat" \
--action setattr \
--xml "w:val=yyyy年MM月dd日"
# US: w:val=MM/dd/yyyy
# ISO: w:val=yyyy-MM-dd (already the default)
# Long: w:val="MMMM d, yyyy"
officecli get "$FILE" '/body/sdt[N]' # expect: type=date format=yyyy年MM月dd日
setattr 用于替换单个属性——不要在 --xml 中为值加引号。只会修改 w:val,<w:dateFormat> 外层元素会被保留。
raw-set 操作与错误
--action | 表单用途 |
|---|---|
append | 在目标末尾插入新的子元素(B1、B2 — listItem) |
setattr | 修改一个属性;--xml "key=value"(B3 — dateFormat/@val) |
replace | 替换整个目标(较少使用 — 重置完整的 <w:date> 外层元素) |
remove | 删除目标(重新填充前清空选项) |
| 现象 | 修复方法 |
|---|---|
raw-set: 0 element(s) affected | XPath 未匹配。检查 tag 值以及 SDT 是块级还是行内级。可回退到 officecli raw $FILE /document 读取实际 XML。 |
Error: prefix 'w' is not defined | 片段缺少 xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"——--xml 中的每个根元素都需要该声明。 |
| 追加后读回的选项为空 | <w:dropDownList/> 必须已存在(路径 A 的 type=dropdown 会确保这一点)。若不存在,追加操作将无处插入。 |
成功信息同一行出现 VALIDATION: N new error(s) introduced | 你的追加操作引入了不符合 schema 的子元素。即使 raw-set 退出码为 0,也应将其视为需要立即停止并修复的问题。 |
路径 C — Word 模板工作流
对于 CLI 无法表达的字段(签名 picture SDT、真正的 SDT 复选框、placeholderDocPart 提示文本、分组 SDT、自定义富文本样式),先在 Word 中一次性构建骨架,再通过 CLI 填充。
在 Word 中的一次性操作: 文件 → 选项 → 自定义功能区 → 开发工具。开发工具选项卡 → 插入图片 / 复选框 / 分组内容控件 → 右键 → 属性 → 设置标题(alias)和标记(Tag)。另存为 template.docx。
通过 CLI 填充:
cp templates/onboarding_with_signature.docx "$FILE"
officecli open "$FILE"
officecli view "$FILE" forms # see /body/... paths + sdtId values
officecli set "$FILE" '/body/sdt[@sdtId=3]' --prop text="Jane Smith"
officecli set "$FILE" / --prop protection=forms
officecli close "$FILE"
MERGEFIELD(数据驱动轨道)
help docx field 声明了一个包含约 30 个值的 fieldType 枚举,其中包括 mergefield、ref、pageref、seq、if——所有这些都可以通过 CLI 结合其带类型的属性来表达。MERGEFIELD 可与 SDT 共存于同一文件中,但只会被 query field 报告;view forms 不会列出 MERGEFIELD(它们不是用户可填写的字段)。
标准 MERGEFIELD:
officecli add "$FILE" /body --type paragraph --prop text="Dear "
officecli add "$FILE" '/body/p[1]' --type field --prop fieldType=mergefield --prop name=CustomerName
officecli add "$FILE" '/body/p[1]' --type run --prop text=", "
officecli add "$FILE" '/body/p[1]' --type field --prop fieldType=mergefield --prop name=CompanyName
# Readback: "Dear «CustomerName», «CompanyName»"
元素类型简写(等效写法):officecli add "$FILE" '/body/p[1]' --type mergefield --prop name=CustomerName。
常见字段模式
| 模式 | 调用形式 |
|---|---|
| 邮件合并占位符 | --type field --prop fieldType=mergefield --prop name=<FieldName> |
| 带数字格式的邮件合并(金额、百分比) | --type field --prop fieldType=mergefield --prop name=Amount --prop instr='MERGEFIELD Amount \# "#,##0.00"'。对 mergefield 而言,类型化的 format 属性会被忽略(并输出警告);请使用 instr(别名 instruction)嵌入完整的域代码。验证:query "$FILE" field --json | jq '.data.results[].format.instruction' 的输出必须包含 \# 及格式图片。 |
| 带日期格式的邮件合并 | --type field --prop fieldType=mergefield --prop name=StartDate --prop instr='MERGEFIELD StartDate \@ "yyyy-MM-dd"' |
| 书签文本的交叉引用 | --type field --prop fieldType=ref --prop name=<BookmarkName> |
| 书签所在页码的交叉引用 | --type field --prop fieldType=pageref --prop name=<BookmarkName> |
| 自动编号(图 1 / 2 / 3) | --type field --prop fieldType=seq --prop identifier=Figure |
| 页脚中的页码 | --type field --prop fieldType=page |
| "第 X 页,共 Y 页" | 两个域:fieldType=page + fieldType=numpages |
| 条件文本 | --type field --prop fieldType=if --prop expression='{ MERGEFIELD Gender } = "Male"' --prop trueText="Mr." --prop falseText="Ms." |
IF 条件域(可通过命令行表达)
officecli add "$FILE" /body --type paragraph --prop text=""
officecli add "$FILE" '/body/p[last()]' --type field --prop fieldType=if \
--prop expression='{ MERGEFIELD Gender } = "Male"' \
--prop trueText="Mr." --prop falseText="Ms."
officecli add "$FILE" '/body/p[last()]' --type run --prop text=" "
officecli add "$FILE" '/body/p[last()]' --type field --prop fieldType=mergefield --prop name=LastName
# Merge-time result: "Mr. «LastName»" or "Ms. «LastName»"
像 { IF { MERGEFIELD X } = "Y" { REF bm } "fallback" } 这样的嵌套结构无法通过 --prop 链式组合表达。此时需要直接设置手工编写的 <w:fldChar> / <w:instrText> 片段,或在 Word 模板中一次性构建(路径 C)。
回读。 query $FILE field 会列出 /field[N]、指令及 fieldType。view $FILE forms 不会列出 MERGEFIELD(只列出 SDT 和 formfield)——它们是模板阶段的内容,并非终端用户可填写的字段。get $FILE '/body/p[1]' 会渲染带有书名号包裹的字段名。
旧版 FormField
当需要真正的复选框时使用 FormField。对于文本和下拉框,优先使用 SDT。
help docx formfield:type(text/checkbox/check/dropdown)、name(必填,≤ 20 个字符——OpenXML 架构的 MaxLength 限制;add 允许更长的名称,但 validate 会拒绝)、text(仅文本,别名 value)、checked(仅复选框)。
# CHECKBOX — the only real checkbox (SDT type=checkbox is not implemented)
officecli add "$FILE" /body --type formfield --prop type=checkbox \
--prop name=agree_terms --prop checked=false
# TEXT formfield
officecli add "$FILE" /body --type formfield --prop type=text \
--prop name=emp_name --prop text="Enter name"
# DROPDOWN formfield — items NOT settable via CLI; use Word template or SDT Path B
officecli add "$FILE" /body --type formfield --prop type=dropdown --prop name=dept_select
# Read / modify by name (stable) or 1-based index
officecli get "$FILE" '/formfield[agree_terms]'
officecli set "$FILE" '/formfield[agree_terms]' --prop checked=true
officecli set "$FILE" '/formfield[emp_name]' --prop text="Jane Smith"
officecli set "$FILE" '/formfield[dept_select]' --prop text="Engineering"
FormField 路径(/formfield[N] 或 /formfield[<name>])与 SDT 路径(/body/sdt[N])相互独立。两者可以共存;protection=forms 同时覆盖两者。
规模。 已在单个文档中测试过 50 个以上的复选框——formfield 数量没有实际上限;构建和 validate 均保持通过。name ≤ 20 个字符(K13)是唯一的硬性约束。
渲染器说明——formfield 复选框 [RENDERER-BUG]。 LibreOffice 导出 PDF 时偶尔会将 formfield 复选框渲染为 ☐☐(双框)。Word 和 WPS 则渲染为单个可点击的方框(切换为 ☑)。这是 LibreOffice 渲染器的特性,不是 Skill 或文档质量问题——参见 K19。不要在表单中尝试变通处理;如果评审人员对 LibreOffice 生成的 PDF 截图并看到 ☐☐,应归因于 [RENDERER-BUG]。
文档保护与锁定
启用表单保护
officecli set "$FILE" / --prop protection=forms
officecli get "$FILE" / # look for: protectionEnforced=True
保护模式
| 模式 | Word 用户可执行的操作 | CLI 行为 |
|---|---|---|
forms | 仅填写 SDT 与表单字段 | 表单字段(SDT)编辑可用(退出码 0);非字段内容编辑需要 --force 或 raw-set |
readOnly | 仅可阅读 | 非字段编辑需要 --force;raw-set 可绕过 |
comments | 仅可添加批注 | 非字段编辑需要 --force;raw-set 可绕过 |
trackedChanges | 仅可进行修订追踪编辑 | 非字段编辑需要 --force;raw-set 可绕过 |
none | 完全编辑 | 所有操作均可执行 |
关键提示: 文档保护同时限制 Word 用户与 CLI。在 protection=forms 模式下,表单字段(SDT)编辑(set /body/sdt[N] --prop text= / --prop alias=)会成功执行(退出码 0),这正是表单保护的用途。仅非字段内容编辑(例如 set /body/p[N] --prop text=)会被拒绝,并返回 ERROR: Document is protected (mode: forms). … use --force to override;若要强制编辑静态内容,需传入 --force(或使用 raw-set,这是唯一能完全绕过保护的命令)。使用 Query("editable") 可查找 Word 用户仍可填写的字段。
锁定值(可在 add 和 set 上设置)
officecli set "$FILE" '/body/sdt[1]' --prop lock=sdtlocked # content editable; control cannot be deleted
officecli set "$FILE" '/body/sdt[1]' --prop lock=contentlocked # content read-only; control can be deleted
officecli set "$FILE" '/body/sdt[1]' --prop lock=sdtcontentlocked # both locked
# Omit lock entirely → unlocked (default)
--prop lock=... 既可用于 add,也可用于 set,建议在创建控件时直接内联设置。无论输入的大小写如何,回读时都会规范为驼峰式(sdtLocked),两种写法均可接受。
lock 与 protection=forms 的交互
| lock 值 | 已启用 protection=forms | Word 用户能否编辑? | Word 用户能否删除控件? |
|---|---|---|---|
| (无) | 是 | 能 | 能 |
sdtlocked | 是 | 能 | 不能 |
contentlocked | 是 | 不能 | 能 |
sdtcontentlocked | 是 | 不能 | 不能 |
块级 SDT 包裹并设为 contentlocked | 任意 | 不能(无论保护与否,被包裹段落均为只读) | 不能 |
| 任意 | readOnly 模式 | 不能 | 不能 |
块级锁定(包裹段落的 SDT)
protection=forms 是文档级别的设置,管理员一旦取消保护,每个静态段落(免责声明、法律声明、合同条款)都会重新变得可编辑。主模板需要纵深防御:将关键段落包裹在设置了 lock=contentLocked 的块级 <w:sdt> 中,这样即使保护被移除,内容仍保持只读。
officecli add "$FILE" /body --type paragraph \
--prop text="I authorize the above and acknowledge all clauses." --prop size=11 --prop spaceAfter=12pt
PID=$(officecli query "$FILE" paragraph --json | jq -r '.data.results[-1].format.paraId')
# raw-set actions: append | prepend | insertbefore | insertafter | replace | remove | setattr
# No `wrap` action — two-step instead: (1) insertbefore an empty <w:sdt><w:sdtContent/></w:sdt>,
# (2) move the original <w:p> inside by `replace` on the sdtContent with a copy of the paragraph XML.
# Simpler alternative: read the paragraph XML via `officecli raw`, then `replace` the whole <w:p> with <w:sdt>...<w:sdtContent>[original w:p]</w:sdtContent></w:sdt>:
# NOTE: this needs an XML-aware extraction, NOT awk. `raw /document` emits the whole document on ONE line,
# so an awk range like `/paraId=.../,/<\/w:p>/` grabs the entire <w:document> (schema-invalid). Parse the XML
# and pull the single <w:p> by paraId instead:
PARA_XML=$(officecli raw "$FILE" /document | python3 -c '
import sys, xml.etree.ElementTree as ET
xml = sys.stdin.read(); pid = sys.argv[1]
ns = {"w":"http://schemas.openxmlformats.org/wordprocessingml/2006/main","w14":"http://schemas.microsoft.com/office/word/2010/wordml"}
for p, u in ns.items(): ET.register_namespace(p, u)
root = ET.fromstring(xml); key = "{%s}paraId" % ns["w14"]
for p in root.iter("{%s}p" % ns["w"]):
if p.attrib.get(key) == pid:
sys.stdout.write(ET.tostring(p, encoding="unicode")); break
' "$PID")
officecli raw-set "$FILE" /document \
--xpath "//w:p[@w14:paraId='$PID']" \
--action replace \
--xml "<w:sdt xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:w14=\"http://schemas.microsoft.com/office/word/2010/wordml\"><w:sdtPr><w:alias w:val=\"Authorization\"/><w:tag w:val=\"auth_para\"/><w:lock w:val=\"contentLocked\"/></w:sdtPr><w:sdtContent>${PARA_XML}</w:sdtContent></w:sdt>"
可使用 query sdt --json | jq '.data.results[] | select(.format.lock == "contentLocked" and .format.type == "block")' 进行验证。此方法仅适用于法律声明、合规免责声明、保密条款;常规录入字段无需使用。
角色限定字段(多角色表单)
当同一表单由两个角色填写(患者与医生;甲方与乙方)时,对另一角色不得修改的字段使用 lock=contentLocked。在 protection=forms 模式下,contentLocked 的 SDT 在 Word 中显示为只读;由目标角色取消保护(或管理员替换为各角色专用副本)后,才能填写另一部分。
# Patient section — editable (no lock, or sdtlocked to prevent accidental deletion only)
officecli set "$FILE" '/body/sdt[1]' --prop lock=sdtlocked # patient_name
officecli set "$FILE" '/body/sdt[2]' --prop lock=sdtlocked # patient_dob
# Physician section — locked against patient edits
officecli set "$FILE" '/body/sdt[14]' --prop lock=contentLocked # physician_diagnosis
officecli set "$FILE" '/body/sdt[15]' --prop lock=contentLocked # physician_signature
这是医疗问诊表、双方合同以及顺序审批表单的核心模式。
配方 — 含 MERGEFIELD 与签名的合同 / SOW 模板
三个子配方的行映射如下:SDT[1]=project_name,SDT[2]=contract_start,SDT[3]=payment_schedule,SDT[4]=signatory_name(内联)。请在同一 $FILE 上按 (sow-a) → (sow-b) → (sow-c) 顺序执行;每个子配方不超过 20 行,以免某个 shell 转义错误扩散到其他代码块。
配方 (sow-a) 样板、封面与各方信息
创建文件,设置 docDefaults,写入标题与引言,并插入两个 MERGEFIELD 占位符(CustomerName、ContractNo),供后续邮件合并填充。
FILE=sow.docx
officecli create "$FILE"
officecli open "$FILE"
officecli set "$FILE" / --prop title="Statement of Work" \
--prop docDefaults.font="Calibri" --prop docDefaults.fontSize="12pt"
officecli add "$FILE" /body --type paragraph --prop text="Statement of Work" \
--prop style=Heading1 --prop size=20 --prop bold=true --prop spaceAfter=12pt
officecli add "$FILE" /body --type paragraph \
--prop text="This Statement of Work ('SOW') is entered into between the parties identified below and governs the delivery of professional services." \
--prop size=11 --prop spaceAfter=12pt
officecli add "$FILE" /body --type paragraph --prop text="Customer: "
officecli add "$FILE" '/body/p[last()]' --type field \
--prop fieldType=mergefield --prop name=CustomerName
officecli add "$FILE" /body --type paragraph --prop text="Contract #: "
officecli add "$FILE" '/body/p[last()]' --type field \
--prop fieldType=mergefield --prop name=ContractNo
配方 (sow-b) SDT 字段 — 全部通过路径 A 完成
添加三个块级 SDT(项目 / 日期 / 下拉列表),并通过 --after 'find:Client Signature:' 在锚点处插入内联签名 SDT。日期格式与下拉选项直接在 add 时设置,无需 raw-set。
officecli add "$FILE" /body --type sdt --prop type=text \
--prop alias="Project Name" --prop tag=project_name --prop text="Enter project name"
officecli add "$FILE" /body --type sdt --prop type=date \
--prop alias="Contract Start Date" --prop tag=contract_start --prop format="MM/dd/yyyy"
officecli add "$FILE" /body --type sdt --prop type=dropdown \
--prop alias="Payment Schedule" --prop tag=payment_schedule \
--prop items="Full Prepayment,Net 30 Upon Delivery"
officecli add "$FILE" /body --type paragraph --prop text="Client Signature:" \
--prop bold=true --prop spaceBefore=18pt --prop spaceAfter=4pt
officecli add "$FILE" /body --type sdt --prop type=text \
--prop alias="Signatory Name" --prop tag=signatory_name --prop text="Authorized Signatory" \
--after 'find:Client Signature:'
# (Only reach for raw-set if a listItem's stored value must differ from its display text — see Path B.)
配方(sow-c)水印 + 锁定 + 文档保护
添加 CONFIDENTIAL 水印(父级为 /,绝不能是 /body),锁定三个块级 SDT,说明如何锁定行内 signatory_name SDT(路径需先通过 view forms 获取),最后以 protection=forms 作为最后一条命令封存文档。
officecli add "$FILE" / --type watermark \
--prop text="CONFIDENTIAL" --prop color=FF0000 --prop rotation=315
officecli set "$FILE" '/body/sdt[1]' --prop lock=sdtlocked
officecli set "$FILE" '/body/sdt[2]' --prop lock=sdtlocked
officecli set "$FILE" '/body/sdt[3]' --prop lock=sdtlocked
officecli view "$FILE" forms # copy signatory_name path, then: set '/body/p[@paraId=...]/sdt[1]' --prop lock=sdtlocked
officecli set "$FILE" / --prop protection=forms
officecli close "$FILE"
officecli query "$FILE" field # expect 2 MERGEFIELDs: CustomerName, ContractNo
表单设计原则
控件类型决策树:
Date → type=date | Fixed list → type=dropdown | List + custom → type=combobox
Short text → type=text | Long text → type=richtext | Boolean → formfield checkbox
排版比例。 间距单位陷阱:spaceBefore / spaceAfter / spaceLine 默认以 twips(1/20 pt)为单位,请始终写成 spaceBefore=18pt。
| 元素 | 字号 | 样式 | 间距 |
|---|---|---|---|
| 表单标题(H1) | 20pt | 粗体 | spaceBefore=0pt,spaceAfter=12pt |
| 章节标题(H2) | 14pt | 粗体 | spaceBefore=18pt,spaceAfter=8pt |
| 字段标签 | 11pt | 粗体 | spaceAfter=4pt |
| 说明 / 备注 | 11pt | 斜体,color=666666 | spaceAfter=18pt |
无障碍字号提升。 对于医疗 / 老年 / 注重无障碍的表单,将字段标签和说明提升至 12pt(11pt 默认值对老年用户而言偏小);章节标题保持 14pt。
中日韩表单: 设置 docDefaults.font="Microsoft YaHei",因为 Calibri 不包含中文字形。
字段顺序。 (1) 个人 / 身份信息,(2) 角色 / 分类,(3) 日期,(4) 补充性自由文本,(5) 确认 / 签名。
是/否 + 条件性追加说明(常见于合规 / 医疗问诊表):先放一个 formfield 复选框,随后紧跟一个 richtext SDT,其 alias 承载提示语,例如 --type formfield --prop type=checkbox --prop name=has_cond,然后 --type sdt --prop type=richtext --prop alias="If yes, explain" --prop tag=cond_detail --prop text="If yes, explain here"。
签名区块顺序。 标签单独占一段,SDT 放在下一段(标签设置 spaceBefore=18pt,SDT 设置 spaceAfter=4pt)。切勿使用 Label: SDT 的行内写法,Word 会将两段文本渲染为紧贴在一起,视觉上粘连。
构建顺序。 create+open → 元数据 → 结构(标题、标签段落)→ SDT/formfield 骨架(路径 A 的 4 个属性)→ 路径 B 注入 → 逐字段锁定 → 最后执行 protection=forms → close。
页眉 / 页脚说明。 页眉/页脚在创建节时即已预定义(default/first/even 各 3 个)。首次修改必须对已存在的部件使用 set,而不是 add;add $FILE /header ... 会返回 already exists 或静默无效。应先执行 officecli query "$FILE" header --json 读取 type 值,再执行 officecli set "$FILE" '/header[@type=default]' --prop text=...。仅当需要为额外节创建独立的页眉/页脚时才使用 add。
批处理模式(简述)
对于控件较多的表单,批处理可减少开销。路径 A 与路径 B 可在同一批次中并存。
cat <<'EOF' | officecli batch "$FILE"
[
{"command":"add","parent":"/body","type":"sdt","props":{"type":"text","alias":"Full Name","tag":"full_name","text":"Enter name"}},
{"command":"add","parent":"/body","type":"sdt","props":{"type":"dropdown","alias":"Department","tag":"dept"}},
{"command":"raw-set","part":"/document","xpath":"//w:sdt[w:sdtPr/w:tag/@w:val='dept']/w:sdtPr/w:dropDownList","action":"append","xml":"<w:listItem xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:displayText=\"Engineering\" w:value=\"Engineering\"/><w:listItem xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:displayText=\"Finance\" w:value=\"Finance\"/>"},
{"command":"set","path":"/body/sdt[1]","props":{"lock":"sdtlocked"}},
{"command":"set","path":"/body/sdt[2]","props":{"lock":"sdtlocked"}}
]
EOF
officecli set "$FILE" / --prop protection=forms
- 在
xml中需转义内部的",写作\"。使用单引号包裹的 heredoc<<'EOF',以免$var被展开。 - P0 批处理陷阱: 批处理中真正不受支持的属性会被静默丢弃,不会输出 WARNING(交互式
add会输出 WARNING: UNSUPPORTED 并以退出码 2 结束)。防护方法:只发送help docx sdt所列出的属性(type/tag/alias/text/items/format/lock/...可用于add;maxlength不可用),并在批处理后通过回读进行验证。 batch支持add、set、get、query、remove、validate、raw-set。
交付门禁(可执行)
每张表单完成后都要运行以下全部门禁。每个门禁都必须输出其 OK 行。任何 REJECT 均表示不得交付。
# Assumes FILE=<your-form.docx>, document has been closed with officecli close "$FILE"
# Gate 1 — Validate (must be clean; protection=forms no longer produces a schema error)
VAL_OUT=$(officecli validate "$FILE" 2>&1)
VAL_ERRS=$(echo "$VAL_OUT" | grep -c '\[Schema\]')
if [ "$VAL_ERRS" -eq 0 ]; then echo "Gate 1 OK (validate clean)"
else echo "REJECT Gate 1: $VAL_ERRS schema errors"; echo "$VAL_OUT"; exit 1
fi
# Gate 2 — Token / placeholder leak (labels used as visual underscore substitutes)
LEAK=$(officecli view "$FILE" text | grep -niE '_{3,}|TBD|\(fill in\)|\{\{|xxxx|lorem|placeholder')
[ -z "$LEAK" ] && echo "Gate 2 OK (no underscore / placeholder leak)" || { echo "REJECT Gate 2:"; echo "$LEAK"; exit 1; }
# Gate 3 — At least one structured field exists
SDT_N=$(officecli query "$FILE" sdt --json | jq '.data.results | length')
FF_N=$(officecli query "$FILE" formfield --json | jq '.data.results | length')
FLD_N=$(officecli query "$FILE" field --json | jq '.data.results | length')
TOTAL=$((SDT_N + FF_N + FLD_N))
[ "$TOTAL" -gt 0 ] && echo "Gate 3 OK ($SDT_N sdt + $FF_N formfield + $FLD_N field)" || { echo "REJECT Gate 3: 0 structured fields — this is not a form"; exit 1; }
# Gate 4 — Every SDT has alias + tag (skill-imposed H2)
# NOTE: `query`/`get --json` wrap prop fields under `.data.results[N].format.{prop}` — use `.data.results[0].format.alias` / `.format.tag`, never bare `.alias` or `.data.format`.
SDT_MISSING=$(officecli query "$FILE" sdt --json | jq '[.data.results[] | select(.format.alias == null or .format.alias == "" or .format.tag == null or .format.tag == "")] | length')
[ "$SDT_MISSING" -eq 0 ] && echo "Gate 4 OK (every SDT has alias+tag)" || { echo "REJECT Gate 4: $SDT_MISSING SDT(s) missing alias or tag"; exit 1; }
# Gate 5 — Protection enforced + per-field lock inventory
PROT=$(officecli get "$FILE" / --json | jq -r '.data.results[0].format.protection // "none"')
[ "$PROT" = "forms" ] && echo "Gate 5 OK (protection=forms enforced)" || { echo "REJECT Gate 5: protection is '$PROT', expected 'forms'"; exit 1; }
officecli view "$FILE" forms | head -40 # visual spot-check: every dropdown shows items=; every date shows format=; every locked SDT shows lock=
# Gate 6 — No type=checkbox leaked onto any SDT
BAD_CB=$(officecli query "$FILE" sdt --json | jq '[.data.results[] | select(.format.type == "checkbox")] | length')
[ "$BAD_CB" -eq 0 ] && echo "Gate 6 OK (no SDT checkbox — formfield only)" || { echo "REJECT Gate 6: $BAD_CB SDT with type=checkbox"; exit 1; }
为何 view issues 不作为门禁。 它只运行偏重文字排版的检查(首行缩进、标题字号),并会将每个表单标签都标记为 Body paragraph missing first-line indent,在表单上造成大量误报。本技能中应忽略它。请改用 validate(结构完整性)和 view forms(字段清单)。
已知问题
| # | 问题 | 行为 | 变通方法 |
|---|---|---|---|
| K1 | SDT type=checkbox 未实现 | add ... --type sdt --prop type=checkbox → Error: SDT type 'checkbox' is not implemented,退出码 1(错误信息现已列出支持的类型,包括 group/picture) | 使用 --type formfield --prop type=checkbox |
| K3 | add 时 SDT maxlength 不受支持 | WARNING: UNSUPPORTED: maxlength,退出码 2;元素仍会被创建。(items / format / lock / placeholderText 现在已支持用于 add,仅 maxlength 被拒绝;name 可被接受,但对 SDT 无效果,请改用 alias/tag) | 在下游校验长度;使用 text 设置初始内容 |
| K4 | 创建后无法设置 SDT items / format / type | set --prop items=... → UNSUPPORTED props (use raw-set instead)。(这些属性可在 add 时设置,请在创建时内联设置) | 在 add 时设置;如需稍后修改,使用路径 B raw-set,或 remove 后重新 add |
| K5 | FormField maxlength 不受支持 | WARNING: UNSUPPORTED: maxlength;formfield 仍被创建 | 在下游校验中限制长度 |
| K6 | FormField 下拉框 items 不受支持 | 创建的下拉 formfield 选项列表为空 | 改用带 --prop items= 的 SDT 下拉框 |
| K7 | 水印 width / height 无法设置 | 水印创建时不带这些属性;opacity 可在 add 时设置并可读回(实际尺寸会自动计算) | 如需精确尺寸,请打开 Word 手动调整形状(第二阶段) |
| K9 | 批处理模式会静默丢弃确实不受支持的属性 | 没有 WARNING 行;即使某属性(例如 maxlength)被丢弃,批处理仍报告“N succeeded” | 批处理中的 SDT 条目仅使用 help docx sdt 列出的属性;批处理后通过回读进行验证 |
| K13 | FormField name 超过 20 个字符 | add 返回退出码 0,且没有任何警告;之后 validate 会在 /w:ffData/w:name 上报告 [Schema] ... MaxLength=20 | 将 name 保持在 20 个字符以内(OpenXML 架构限制)。SDT 的 alias / tag 没有此限制 |
| K14 | 段落上的 shd.fill 会生成架构无效的 <w:pPr>/<w:shd> | validate 每个实例报告 2 个架构错误(unexpected child element、required attribute 'val' missing);Word 仍会照常渲染 | 改为在文本块(run)上应用高亮(shading=HEX,扁平规范形式),或将 <w:shd w:val="clear" w:fill="HEX"/> 通过 raw-set 写入文本块的 <w:rPr> |
| K15 | view forms 不会列出 MERGEFIELD | 输出中仅包含 SDT 和 formfield;MERGEFIELD 是模板阶段的内容,不可由最终用户填写 | 将 query field 和 view forms 视为两个互不重叠的清单。每个方案都要同时验证两者 |
| K16 | 页眉 / 页脚在创建节时已预定义(default/first/even,各 3 个) | add $FILE /header ... 首次调用时返回 already exists 或静默无操作 | 首次修改应使用 set 作用于已有部件:先运行 officecli query $FILE header --json 读取 type,然后执行 set '/header[@type=default]' --prop text=...。仅在为全新节添加页眉/页脚时才使用 add |
| K17 | 注入页眉的水印会生成架构无效的 <w:noProof> 子元素 | validate 会在 /header[N]/w:sdt/.../w:noProof 处额外报告一个 [Schema] 错误——这是需要修复的真实架构错误,而非无害问题 | 执行 add $FILE / --type watermark 后,对每个页眉部件运行一次:officecli raw-set $FILE /word/header1.xml --xpath "//w:noProof" --action remove(若存在 header2.xml、header3.xml,同样重复执行) |
| K18 | query/get --json 会将属性字段包裹在 .data.results[N].format.{prop} 下 | 对裸 .alias / .tag / .protection 或 .data.format.* 编写 jq,会返回 null/0 匹配,导致 Gate 4/5 误报 "missing=N" | 使用 .data.results[].format.alias / .format.tag;对于 get /,使用 .data.results[0].format.protection(而非 .data.format.protection)。.format.type / .format.paraId 同理 |
| K19 | LibreOffice 在导出 PDF 时将 formfield 复选框渲染为 ☐☐(双框) | 仅为外观问题——Word / WPS 渲染为单个方框,可点击切换为 ☑。这是 LibreOffice 渲染器的特性,标记为 [RENDERER-BUG] | 不要尝试在本技能中“修复”。若评估者从 LibreOffice 生成的 PDF 截图中看到 ☐☐,应归因于 [RENDERER-BUG],而非表单质量缺陷 |
第二阶段:在 Word 中增强
部分优化超出了 CLI 的范围。以下内容需交由人工在 Word 中完成;这些都不是生成有效表单所必需的。
| 需求 | 为何要打开 Word |
|---|---|
| 具有特定锁定设置的真实 SDT 复选框 | type=checkbox 会以退出码 1 结束;请使用“开发工具” → “复选框内容控件”(或通过 CLI 添加旧版 FormField 复选框) |
| 提示文本(“单击此处输入日期”) | 需要在 /word/glossary/document.xml 中设置 placeholderDocPart |
| 自定义富文本默认外观 | 在 Word 的样式窗格中调整所引用的样式 |
| 水印尺寸调整 | 无法设置 width / height;请拖动形状控制点 |
(picture 和 group 类型的 SDT 可直接通过 --type sdt --prop type=picture / type=group 添加,不再属于第二阶段的事项。)
前四项请先按路径 C 构建一次骨架,然后重复使用。
帮助指引
如有疑问,请运行:officecli help docx、officecli help docx <element>、officecli help docx <element> --json。帮助信息是权威的结构定义;本技能则是在其之上构建真实可填写 Word 表单的决策指南。