From 3060da65baa4028bd889a1cb5cfc90e810526577 Mon Sep 17 00:00:00 2001 From: mizzlelover Date: Thu, 10 Sep 2026 21:23:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=A5=E6=A0=BC=E8=90=BD=E5=AE=9E=E5=85=AC?= =?UTF-8?q?=E6=96=87=E7=89=88=E5=BC=8F=E5=85=A8=E8=A6=81=E7=B4=A0=E6=A0=B8?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +- SKILL.md | 8 +- dist/traework-gongwen-skill.zip | Bin 27791 -> 35384 bytes references/formal-checklist.md | 3 +- references/gbt9704-2012-summary.md | 4 +- references/gbt9704-audit-matrix.md | 50 ++++++++ scripts/generate_gongwen_docx.mjs | 200 +++++++++++++++++++++++------ scripts/verify_gongwen_docx.mjs | 39 +++++- tests/run_tests.sh | 29 ++++- 9 files changed, 285 insertions(+), 55 deletions(-) create mode 100644 references/gbt9704-audit-matrix.md diff --git a/README.md b/README.md index 0daa2cc..3429a2b 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ https://std.samr.gov.cn/gb/search/gbDetailed?id=lOIe27f77QU%3D&mode=p | 普通报告、方案、汇报 | 默认 `ordinary`,不因机构名称自动使用红头 | | 正式发文与红头纸 | 显式 `formal`;默认预印红头纸套打,首面留白、不重绘红色机关标志和红线 | | 完整电子红头 | 显式 `formal --letterhead digital`,才在 DOCX 中绘制红头要素 | -| 机构名称与文号 | 使用 `--org`、`--doc-no` 按输入排版;文号仅在相应版式中定位 | +| 机构名称与文号 | 使用 `--org`、`--doc-no` 按对应版式定位;正式版式会拒绝明显不符合年份、六角括号、顺序号和“号”规则的文号 | | 标题与目录 | 四级标题写入 Word/WPS 标题样式及大纲级别,可插入自动目录 | | 页码 | 居中、单双页或不显示 | | 印章、签名章 | 在生成的 DOCX 中按实际需要插入图片或完成实体盖章 | @@ -104,9 +104,9 @@ node scripts/generate_gongwen_docx.mjs --input tests/fixture.md --output /tmp/fo node scripts/verify_gongwen_docx.mjs --input /tmp/formal.docx --profile formal --letterhead preprinted ``` -只有需要完整电子红头文件时,才加 `--letterhead digital`。红头和标题要使用目标电脑中实际安装的小标宋体;生成器缺少小标宋体或仿宋体时会在终端明确警告并写出替代字体,要求严格阻止替代输出时加 `--require-standard-fonts`。红线下标题会按版心网格预留两行,不会贴着分隔线;上行文的文号和签发人会在同一行编排。打开文档后,Word/WPS 的“引用→目录”可以按“标题1—标题4”生成并更新目录;是否显示第四级,在目录设置中选择。 +只有需要完整电子红头文件时,才加 `--letterhead digital`。红头和标题要使用目标电脑中实际安装的小标宋体;生成器缺少小标宋体或仿宋体时会在终端明确警告并写出替代字体,要求严格阻止替代输出时加 `--require-standard-fonts`。红线下标题会写入两个显式的28磅版心空行,不会依赖渲染器自行解释段前距;发文机关标志到文号也按相同方式留出两行。上行文的文号和签发人会在同一行编排。打开文档后,Word/WPS 的“引用→目录”可以按“标题1—标题4”生成并更新目录;是否显示第四级,在目录设置中选择。 -信函、命令(令)和纪要走专用分支:信函生成170mm上粗下细、下页边20mm处上细下粗的两条红色双线,底线放在页脚并关闭页码;命令(令)落实机关标志下空二行、令号下空二行;纪要把“出席”“请假”“列席”标签设为黑体、人员名单设为仿宋。附件可用重复的 `--attachment-file` 另页生成“附件”标签、第三行标题和正文,并保持正式页码连续。 +信函、命令(令)和纪要走专用分支:信函生成170mm上粗下细、下页边20mm处上细下粗的两条红色双线,底线放在页脚并关闭页码;命令(令)落实机关标志下空二行、令号下空二行;纪要把“出席”“请假”“列席”标签设为黑体、人员名单设为仿宋。附件可用重复的 `--attachment-file` 另页生成“附件”标签、第三行标题和正文,并保持正式页码连续。带抄送或印发信息时,版记浮动锚定在最后一页版心底部,首末粗线约0.35mm、中间细线约0.25mm。 ## 跨平台安装 @@ -130,6 +130,7 @@ Windows: - [SKILL.md](SKILL.md):使用指引与版式参数。 - [标准执行摘要](references/gbt9704-2012-summary.md):国家标准的可执行要点。 +- [逐条执行矩阵](references/gbt9704-audit-matrix.md):按条款记录自动生成、结构校验和必须人工复核的边界。 - [正式公文核验表](references/formal-checklist.md):模板完成后的全要素检查。 - [生成器](scripts/generate_gongwen_docx.mjs):根据输入生成 DOCX 版式。 - [校验器](scripts/verify_gongwen_docx.mjs):检查生成器可承诺的 DOCX 版式要素。 diff --git a/SKILL.md b/SKILL.md index 728913b..d44e2d0 100644 --- a/SKILL.md +++ b/SKILL.md @@ -9,13 +9,13 @@ description: 将中文正式材料生成参照 GB/T 9704-2012 的公文版式 DO GB/T 9704-2012《党政机关公文格式》现行,2025-05-30 复审继续有效。其适用于党政机关制发公文;其他机关、单位可以参照执行。 -先读 `references/gbt9704-2012-summary.md`;需要逐项复核时再读 `references/formal-checklist.md`。标准规定纸张、版面、要素编排和印制装订要求。 +先读 `references/gbt9704-2012-summary.md`;需要逐项复核时再读 `references/gbt9704-audit-matrix.md` 和 `references/formal-checklist.md`。标准规定纸张、版面、要素编排和印制装订要求。 ## 工具定位 本工具只处理排版,不判断单位身份、授权状态或后续盖章方式。它会根据用户明确选择的版式模式应用对应的排版规则,不因“正式”“国企”“报告”或仅提供机构名称而擅自使用红头。 -生成器可输出 A4、156mm×225mm 版心、三号仿宋正文、小标宋二号标题、四级标题层次、两字缩进,以及居中或单双页页码。四级标题均写入 Word/WPS 标题样式和大纲级别,便于后续插入、更新目录;文章总标题使用独立“公文标题”样式,不进入目录。上行文的文号和签发人在同一行编排;信函、命令(令)、纪要使用各自专用版式;重复传入 `--attachment-file` 时,附件会另页生成并保持页码连续。 +生成器可输出 A4、156mm×225mm 版心、三号仿宋正文、小标宋二号标题、四级标题层次、两字缩进,以及居中或单双页页码。四级标题均写入 Word/WPS 标题样式和大纲级别,便于后续插入、更新目录;文章总标题使用独立“公文标题”样式,不进入目录。上行文的文号和签发人在同一行编排;信函、命令(令)、纪要使用各自专用版式;重复传入 `--attachment-file` 时,附件会另页生成并保持页码连续。版记会锚定最后一页版心底部,正式页码按版心下边缘下7mm的位置生成。 `references/formal-checklist.md` 是可选的版式复核清单。涉及印章或签名章时,使用者可在 Word/WPS 中插入已取得的图片或完成实体盖章;本生成器不内置印章图像。 @@ -37,7 +37,9 @@ node scripts/generate_gongwen_docx.mjs --input source.md --output output.docx -- `letter`、`command`、`minutes` 分别对应信函、命令(令)、纪要格式;需要时显式传入 `--format` 和相应字段。正式发文默认单双页页码;`standard` 为单页右、双页左,`center` 与 `none` 供普通材料选择。 -附件可重复传入 `--attachment-file attachment.md`。附件 Markdown 的首个一级标题会作为附件标题,第一页第一行写“附件1”(后续文件递增),第三行写标题,随后排正文;附件说明仍通过 `--attachment-note` 提供。复杂横排附件、不能与正文一起装订的附件和机关专用模板仍需在目标 Word/WPS 中核对。 +附件可重复传入 `--attachment-file attachment.md`。附件 Markdown 的首个一级标题会作为附件标题,第一页第一行写“附件1”(后续文件递增),第三行写标题,随后排正文;附件说明仍通过 `--attachment-note` 提供,工具会统一为“附件:”并去除末尾标点。复杂横排附件、不能与正文一起装订的附件和机关专用模板仍需在目标 Word/WPS 中核对。 + +正式版式的 `--doc-no` 采用“单位发〔2026〕1号”这类标准写法;`--date`、`--print-date` 采用“2026年9月9日”,月份和日期不补零。明显不符合这两类规则的输入会被拒绝,避免把错误字段排成看似规范的文件。 Markdown 标题优先按序数识别:`一、`、`(一)`、`1.`、`(1)`;序数缺失时才按 Markdown 层级映射。正文、四级标题均左空二字,回行顶格。标题字体在运行机器缺失时会明确报告并使用可见替代字体;打开文件时仍须确认目标 Office/WPS 的实际字体,不把普通宋体视作小标宋的等价替代。 diff --git a/dist/traework-gongwen-skill.zip b/dist/traework-gongwen-skill.zip index f629e5d7c328ef49f909b6f053b925623b968c35..253850f633a2347f28a47bb2bcd58a46c8966670 100644 GIT binary patch delta 32813 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 H|55rsFfa%s delta 25166 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 diff --git a/references/formal-checklist.md b/references/formal-checklist.md index b886832..f3ccfe8 100644 --- a/references/formal-checklist.md +++ b/references/formal-checklist.md @@ -21,6 +21,7 @@ - [ ] 发文字号位于机关标志下空二行;年份为全称阿拉伯数字并使用六角括号〔〕,序号不加“第”、不补零、后加“号”。 - [ ] 上行文文号左空一字;签发人与文号在同一行同一区域,右空一字。“签发人:”为仿宋三号,姓名为楷体三号;多签发人按机关顺序编排。 - [ ] 红色分隔线在文号下4mm处,与版心等宽,线高按模板核定(常用约0.35—0.5mm)。 +- [ ] 用 PDF 或 Word/WPS 实测确认机关标志到文号、红线到标题均为两个版心网格空行;不能只看 XML 中是否存在某个段前距数值。 ## 3. 主体 @@ -37,7 +38,7 @@ ## 4. 版记、页码与特殊格式 -- [ ] 版记首末分隔线为粗线、中间线为细线;末条线与最后一面版心下边缘重合。 +- [ ] 版记首末分隔线为粗线、中间线为细线;生成器会把版记锚定到最后一面版心下边缘,仍需在长文、多表格和 Word/WPS 中抽检。 - [ ] 抄送机关适用时为四号仿宋、左右各空一字、回行与冒号后首字对齐、末尾句号;主送移入版记时按“主送”规则处理。 - [ ] 印发机关和日期为四号仿宋,左空一字与右空一字;日期完整且不补零,后加“印发”。 - [ ] 页码为四号半角宋体阿拉伯数字,两侧各一字线,线距版心下边缘7mm;单页右空一字、双页左空一字;空白页、特定版记页和附件连续页码已按规则处理。 diff --git a/references/gbt9704-2012-summary.md b/references/gbt9704-2012-summary.md index a8cebc6..f66c322 100644 --- a/references/gbt9704-2012-summary.md +++ b/references/gbt9704-2012-summary.md @@ -112,7 +112,7 @@ - 抄送机关一般用4号仿宋,左右各空一字;回行与冒号后首字对齐,末尾用句号。 - 印发机关左空一字,印发日期右空一字,日期不补零,后加“印发”。 - 版记分隔线有粗细区分,并与版心等宽。 -- 版记字段可按实际需要在生成后的文件中补充或调整。 +- 版记字段在生成器中锚定到最后一页版心底部;字段内容、超长抄送回行和机关专用模板仍需在目标 Word/WPS 中复核。 ## 七、页码 @@ -146,7 +146,7 @@ | A4、版心、字体、行距、正文缩进 | 是 | 打印设备误差复核 | | 四级正文标题 | 是 | 特殊机关模板调整 | | 报告居中页码 | 是 | 无 | -| 正式单双页页码 | 是 | `--page-number standard` | +| 正式单双页页码 | 是 | `--page-number standard`;页脚按版心下边缘下7mm的位置生成 | | 普通表格 | 是 | 横排表格方向和复杂跨页表格复核 | | 普通材料 | 是 | `--format ordinary`,不绘制红头 | | 预印红头纸套打 | 是 | `--format formal --letterhead preprinted`,按纸样设置预留高度 | diff --git a/references/gbt9704-audit-matrix.md b/references/gbt9704-audit-matrix.md new file mode 100644 index 0000000..7bd9adf --- /dev/null +++ b/references/gbt9704-audit-matrix.md @@ -0,0 +1,50 @@ +# GB/T 9704-2012 逐条执行矩阵 + +本矩阵把标准条款拆成三类:生成器可以稳定写入并由校验器检查的项目,生成器能够提供版式但仍需打开 Word/WPS 复核的项目,以及只能在纸张、打印或机关模板上完成的项目。它不把“生成了 DOCX”写成“所有制作环节均已合规”。条文依据为[全国标准信息公共服务平台的现行状态页](https://std.samr.gov.cn/gb/search/gbDetailed?id=lOIe27f77QU%3D&mode=p)和[GB/T 9704-2012 条文全文转载页](https://dzb.cumtb.edu.cn/info/1040/1184.htm)。 + +| 条款 | 规则 | 当前实现 | 证据/剩余人工核对 | +| --- | --- | --- | --- | +| 1 | 适用范围 | 说明文档明确 `ordinary`、`formal` 和特定格式的使用场景,不因机构名称自动画红头 | `SKILL.md`、`README.md`;仍由使用者确认文种和机关模板 | +| 2 | 规范性引用文件 | 在摘要和清单中列出 GB/T 148、GB 3100/3101/3102、GB/T 15834、GB/T 15835 | `references/gbt9704-2012-summary.md`;计量单位、标点和数字的语义仍需人工复核 | +| 3 | 字、行等术语 | 以 3 号字和 28 磅版心网格实现常用“行” | `w:docGrid`、正文段落;特殊机关模板可调整 | +| 4 | 用纸技术指标 | 不在 DOCX 中伪造纸张克重、白度、耐折度、不透明度、pH | `formal-checklist.md`;采购纸张和成品检测人工完成 | +| 5.1 | A4 成品幅面 | 固定 210mm×297mm | `verify_gongwen_docx.mjs` 的 A4 检查;仍需检查实际裁切误差 | +| 5.2.1 | 天头、订口、版心 | 37mm、28mm、156mm×225mm写入 DOCX | 版心和页边检查;打印缩放必须关闭并现场复核 | +| 5.2.2 | 默认 3 号仿宋 | 运行环境检测仿宋;缺失时明确警告并写入替代字体,严格模式直接失败 | `--require-standard-fonts`;当前机器若缺小标宋/仿宋不能把替代字体当标准字体 | +| 5.2.3 | 一般 22 行、28 字并撑满版心 | 3 号、28 磅固定网格和版心参数已写入 | `w:docGrid` 与 PDF 实测;长标题、表格和特殊内容仍需看最终分页 | +| 5.2.4 | 默认黑色 | 正文、标题层级、字段默认为黑色,红色只用于规定的版头/特定格式/版记线 | 生成器和 PDF 视觉检查 | +| 6.1 | 制版清洁、字迹、尺寸、版心误差 | 不在 XML 中虚报制版质量 | PDF/打印成品人工检查 | +| 6.2 | 双面印刷、套正、油墨和印品质量 | 正式模式生成单页右、双页左页码;不控制打印机和油墨 | `--page-number standard`;双面打印和套正人工检查 | +| 6.3 | 左侧装订、钉位、裁切和成品 | 不生成虚假的装订结果 | `formal-checklist.md`;成品人工检查 | +| 7.1 | 版头、主体、版记三区和页码在版心外 | 版式分支、页脚和版记结构分别生成 | DOCX/PDF 逐页检查 | +| 7.2.1 | 份号六位、3 号、左上第一行 | `--copy-no` 只接受 1—6 位阿拉伯数字并补足六位 | 结构检查;实际是否需要份号由发文机关确认 | +| 7.2.2 | 密级和保密期限第二行、3 号黑体 | `--secret` 按黑体 3 号写入 | 文字内容和期限写法人工复核 | +| 7.2.3 | 紧急程度及排列顺序 | `--urgent` 按黑体 3 号写入,并按份号、密级、紧急程度顺序下移 | 适用性和紧急级别人工确认 | +| 7.2.4 | 发文机关标志居中、红色、版心上边缘下 35mm、小标宋 | `formal --letterhead digital` 才绘制;预印模式只留纸样区域;联合行文仍需模板 | `verify_gongwen_docx.mjs`、PDF;目标电脑字体和联合行文人工核对 | +| 7.2.5 | 文号下空二行、六角括号、全年份、顺序号不加“第”不补零 | `formal`/`letter` 对 `--doc-no` 做格式校验,并写入两个显式 28 磅空行 | 文号语义由机关编号规则确认 | +| 7.2.6 | 上行文签发人同文号一行;标签仿宋、姓名楷体 | `--upward true --signer` 同行生成单一签发人;多签发人、联合行文转人工模板 | 结构校验和 PDF;多个签发人需按机关顺序人工排布 | +| 7.2.7 | 文号下 4mm、版心等宽红线 | `redRule()` 生成 156mm 红线,文号段后 4mm;预印模式不重绘 | PDF 量测;红色油墨和实际纸样人工核对 | +| 7.3.1 | 标题 2 号小标宋,红线下空二行、居中 | 标题使用 `GongwenTitle`,红线/预印线后插入两个显式 28 磅空段落 | XML + PDF 视觉门;小标宋缺失时严格模式失败 | +| 7.3.2 | 主送机关标题下空一行、顶格、全角冒号 | `--to` 或首段识别后写入一行并规范化末尾全角冒号 | 过多主送机关移版记仍需人工决定 | +| 7.3.3 | 首页有正文;正文 3 号仿宋、自然段左空二字;四级序数和字体 | Markdown 序数映射 `Heading1`—`Heading4`、大纲级别和字体已写入 | Word/WPS 目录可引用;特殊层次和分页人工复核 | +| 7.3.4 | 附件说明正文下空一行、左空二字、名称末无标点 | `--attachment-note` 统一为“附件:”并去除末尾标点,表格后不重复空行 | 长名称回行和多附件编号仍需 PDF/模板复核 | +| 7.3.5 | 署名、成文日期、印章/签名章 | 不加章署名日期按左右空字规则生成;日期校验为全年月日且月日不补零;不伪造印章 | `--seal-mode` 仍需把实体章或已取得的图片放入 Word/WPS;联合行文人工完成 | +| 7.3.6 | 附注在日期下一行左空二字、圆括号 | `--note` 规范化为全角圆括号 | 日期与附注的最终分页人工核对 | +| 7.3.7 | 附件另面、第一行附件标签黑体、第三行标题、版记前、页码连续 | `--attachment-file` 用分页符、显式空行和标题样式生成,附件共用连续页脚 | 不能一起装订时的文号+附件标识、复杂附件版式人工处理 | +| 7.4.1 | 版记首末粗线、中间细线,末线贴最后一页版心下边缘 | 版记改为浮动表,锚定最后一页版心底部;粗线 0.35mm、细线 0.25mm | 已用长文 PDF 截图实测;Word/WPS 和极端分页仍需抽检 | +| 7.4.2 | 抄送 4 号仿宋、左右一字、回行对齐、末尾句号 | `--cc` 生成 4 号、左右一字、全角冒号和句号 | 多行抄送回行和主送移版记人工复核 | +| 7.4.3 | 印发机关/日期 4 号仿宋,左右一字,日期后“印发”,细线隔开 | `--print-org`、`--print-date` 生成右制表位、日期校验和末条粗线 | 实际机关名称长度和目标软件分页人工复核 | +| 7.5 | 4 号半角宋体页码、两侧一字线、距版心下边缘 7mm、单双页位置、附件连续 | 正式页脚用半角宋体、单双页字段,footer 距离调整为 7mm 位置;信函关闭页码 | XML + PDF bbox;空白页/特定版记页的例外仍需人工复核 | +| 8 | 横排表格页码与表头方向 | 普通 Markdown 表格按纵向版心生成,不假装自动完成横排 | 横排表格须在 Word/WPS 采用专用横向模板并人工检查 | +| 9 | 计量单位、标点、数字按引用标准 | 结构字段(文号、日期、附件冒号、句号)做边界校验 | 正文语义、单位、标点和数字用法需人工或专门文本审校 | +| 10.1 | 信函上下双红线、30mm/20mm、170mm、标题二行、无首页页码、无版记线 | `--format letter` 生成双线、专用边距、文号距离、标题空行、底部双线页脚和无页码 | PDF 实测;联合行文及模板字段人工复核 | +| 10.2 | 命令(令)标志上边缘 20mm、令号下空二行、正文下空二行 | `--format command` 要求 `--org`、`--doc-no`,两个间隔均为显式 28 磅空行 | 令号文字和签名章/日期按专用模板人工复核 | +| 10.3 | 纪要标志 35mm;出席/请假/列席标签黑体,人员仿宋 | `--format minutes` 要求机关标志以“纪要”结尾,三类人员分 run 写入对应字体 | 纪要具体版式可按机关制度调整 | +| 11 | 标准式样图 | 各分支按条款参数实现,不把截图当作唯一依据 | 最终仍需与机关现行式样、Word/WPS 和打印样张逐页比对 | + +## 本轮硬性修正 + +- 红线下标题不再只依赖 `w:before` 的段前属性,而是写入两个显式、28 磅、无文字的版心网格段落;这解决了不同 Word/WPS 渲染时标题贴线的问题。 +- 发文机关标志到文号、命令标志到令号、令号到正文同样使用显式空行;校验器不再把“存在某个段前数值”当成视觉合规证据。 +- 版记不再跟在正文最后一行后面,而是锚定到最后一页版心底部;正式页码 footer 改为版心下边缘下 7mm 的位置。 +- 文号、成文日期、附件说明的结构边界现在会拒绝明显不符合标准的输入;缺失小标宋或仿宋时仍会明确告警,严格模式会停止生成。 diff --git a/scripts/generate_gongwen_docx.mjs b/scripts/generate_gongwen_docx.mjs index 89410e4..6d03bdc 100755 --- a/scripts/generate_gongwen_docx.mjs +++ b/scripts/generate_gongwen_docx.mjs @@ -103,9 +103,10 @@ Notes: - ordinary is the default for reports, plans and other editable materials. It never creates a red header from --org alone. - formal is used only when a formal issuing-document layout is explicitly requested. It defaults to preprinted letterhead: red elements are reserved, not redrawn. Use --letterhead digital only for a complete electronic red-head layout. - letter, command and minutes use their dedicated national-standard layout branches. Their required fields must be supplied explicitly. - - --org and --doc-no are printed exactly as supplied. The tool formats document content; it does not infer missing information or decide the document's use. + - formal and letter document numbers are checked for full-year〔〕sequence号 syntax; dates are checked for YYYY年M月D日 without zero padding. + - --org and --doc-no are printed as supplied after boundary validation. The tool formats document content; it does not infer missing information or decide the document's use. - The generator reports when the required small-standard-title or FangSong font is unavailable and uses a visible fallback. Add --require-standard-fonts to refuse generation until the required font is installed. - - Supports headings, paragraphs, ordered/unordered lines, pipe tables, and repeatable Markdown attachment pages via --attachment-file. + - Supports headings, paragraphs, ordered/unordered lines, pipe tables, and repeatable Markdown attachment pages via --attachment-file. The colophon is anchored to the bottom of the final page's type area. - No npm dependencies; requires zip in PATH.`); } @@ -198,14 +199,14 @@ function titleParagraph(text, before = "0") { }); } -function agencyMarkParagraph(text, before = "0") { +function agencyMarkParagraph(text, before = "0", after = "0") { return paragraph(text, { align: "center", indent: false, fontPreset: "title", size: "56", color: "FF0000", - after: "560", + after, before, }); } @@ -237,7 +238,10 @@ function documentNumberParagraph(text, upward = false) { function upwardHeaderParagraph(docNo, signer) { const tabPosition = CONTENT_W - FIRST_LINE_INDENT; - return `${run(docNo, { fontPreset: "body", size: "32" })}${run("签发人:", { fontPreset: "body", size: "32" })}${run(signer, { fontPreset: "h2", size: "32" })}`; + const signers = String(signer).split(/[、,,;;\s]+/).map((name) => name.trim()).filter(Boolean); + if (signers.length > 2) throw new Error("upward --signer supports up to two names; use the authority's joint-signature template for more signers"); + const signerRuns = signers.map((name, index) => `${index ? run("  ", { fontPreset: "h2", size: "32" }) : ""}${run(name, { fontPreset: "h2", size: "32" })}`).join(""); + return `${run(docNo, { fontPreset: "body", size: "32" })}${run("签发人:", { fontPreset: "body", size: "32" })}${signerRuns}`; } function subtitleParagraph(text) { @@ -264,6 +268,38 @@ function normalizeMainSend(text) { return value && /[::]$/.test(value) ? value : `${value}:`; } +function normalizeDocumentNumber(text, label = "--doc-no") { + const value = String(text ?? "").trim(); + if (!value) return value; + if (!/^[^〔〕\[\]【】]+〔\d{4}〕[1-9]\d*号$/.test(value) || /第/.test(value)) { + throw new Error(`${label} must use 年份全称、六角括号、非虚位顺序号且以“号”结尾,例如“示例发〔2026〕1号”`); + } + return value; +} + +function normalizeChineseDate(text, label = "--date", allowPrintSuffix = false) { + const raw = String(text ?? "").trim(); + const value = allowPrintSuffix ? raw.replace(/印发$/, "") : raw; + const match = value.match(/^(\d{4})年((?:[1-9]|1[0-2]))月((?:[1-9]|[12]\d|3[01]))日$/); + if (!match) throw new Error(`${label} must use YYYY年M月D日 and must not zero-pad month/day`); + const year = Number(match[1]); + const month = Number(match[2]); + const day = Number(match[3]); + const date = new Date(Date.UTC(year, month - 1, day)); + if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) { + throw new Error(`${label} is not a valid calendar date`); + } + return value; +} + +function normalizeAttachmentNote(text) { + const value = String(text ?? "").trim().replace(/[。;;]+$/, ""); + if (!value) throw new Error("--attachment-note cannot be empty"); + const names = value.replace(/^附件[::]\s*/, "").trim(); + if (!names) throw new Error("--attachment-note must contain an attachment name after 附件:"); + return `附件:${names}`; +} + function h1(text) { return paragraph(text, { align: "left", @@ -320,6 +356,10 @@ function right(text) { return paragraph(text, { align: "right", indent: false, right: FIRST_LINE_INDENT }); } +function rightWithIndent(text, rightIndent) { + return paragraph(text, { align: "right", indent: false, right: String(rightIndent) }); +} + function redRule(thickness = "5") { const height = thickness === "thick" ? "0.35mm" : thickness === "thin" ? "0.25mm" : "0.5mm"; return ``; @@ -341,31 +381,94 @@ function minutesPersonParagraph(label, people, before = "0") { } function headerFieldParagraph(text, fontPreset = "h1", opts = {}) { - return paragraph(text, { align: opts.align ?? "left", indent: false, fontPreset, size: "32", after: opts.after ?? "0", left: opts.left, right: opts.right }); + return paragraph(text, { align: opts.align ?? "left", indent: false, fontPreset, size: "32", before: opts.before ?? "0", after: opts.after ?? "0", left: opts.left, right: opts.right }); } function attachmentNote(text) { - return paragraph(`附件:${text}`, { align: "left", fontPreset: "body", size: "32", before: "560", firstLine: FIRST_LINE_INDENT, left: "0" }); + return paragraph(normalizeAttachmentNote(text), { align: "left", fontPreset: "body", size: "32", before: "560", firstLine: "-960", left: "1600" }); +} + +function estimatedTextWidthDxa(text) { + return [...String(text ?? "")].reduce((sum, char) => sum + (/^[\x00-\x7f]$/.test(char) ? 160 : 320), 0); +} + +function signatureParagraphs(args) { + if (args["seal-mode"] === "signed") { + if (!args.signer || !args["signer-title"]) throw new Error("signed seal mode requires --signer and --signer-title"); + const lines = [rightWithIndent(`${args["signer-title"]} ${args.signer}`, FIRST_LINE_INDENT * 2)]; + if (args.date) lines.push(rightWithIndent(args.date, FIRST_LINE_INDENT * 2)); + return lines; + } + if (args["seal-mode"] === "seal") { + if (!args.sender && !args.date) return []; + const lines = []; + if (args.sender) lines.push(rightWithIndent(args.sender, FIRST_LINE_INDENT * 2)); + if (args.date) lines.push(rightWithIndent(args.date, FIRST_LINE_INDENT * 2)); + return lines; + } + if (!args.sender && !args.date) return []; + if (!args.sender || !args.date) return [right(args.sender || args.date)]; + const senderWidth = estimatedTextWidthDxa(args.sender); + const dateWidth = estimatedTextWidthDxa(args.date); + let senderRight = FIRST_LINE_INDENT; + let dateRight = Math.max(0, senderWidth - dateWidth); + if (dateWidth > senderWidth) { + dateRight = FIRST_LINE_INDENT; + senderRight = dateWidth - senderWidth + FIRST_LINE_INDENT; + } + return [ + paragraph(args.sender, { align: "right", indent: false, right: String(senderRight) }), + paragraph(args.date, { align: "right", indent: false, right: String(dateRight) }), + ]; +} + +function colophonTextRow(text, align = "left") { + return `${paragraph(text, { align, firstLine: "-960", left: "1280", right: "320", fontPreset: "body", size: "28" })}`; +} + +function colophonRuleRow(thickness) { + const heightMm = thickness === "thick" ? "0.35" : "0.25"; + return `${redRule(thickness)}`; } function colophon(args) { if (!args["cc"] && !args["print-org"] && !args["print-date"]) return []; - const body = [redRule("thick")]; - if (args.cc) body.push(paragraph(`抄送:${args.cc.replace(/[。.]?$/, "")}。`, { align: "left", indent: false, left: "320", right: "320", fontPreset: "body", size: "28" })); - if (args.cc) body.push(redRule("thin")); + const body = [colophonRuleRow("thick")]; + if (args.cc) body.push(colophonTextRow(`抄送:${args.cc.replace(/[。..]+$/, "")}。`)); + if (args.cc) body.push(colophonRuleRow("thin")); if (args["print-org"] || args["print-date"]) { const left = args["print-org"] ?? ""; const rightValue = args["print-date"] ? `${args["print-date"].replace(/印发$/, "")}印发` : ""; - body.push(`${run(left, { fontPreset: "body", size: "28" })}${run(rightValue, { fontPreset: "body", size: "28" })}`); + const printRow = `${run(left, { fontPreset: "body", size: "28" })}${run(rightValue, { fontPreset: "body", size: "28" })}`; + body.push(`${printRow}`); } - body.push(redRule("thick")); - return body; + body.push(colophonRuleRow("thick")); + const tablePr = ``; + return [`${tablePr}${body.join("")}`]; } function emptyLine() { return ``; } +function blankGridLine() { + return paragraph("\u00a0", { + align: "left", + indent: false, + fontPreset: "body", + size: "2", + line: "560", + lineRule: "exact", + snapToGrid: false, + before: "0", + after: "0", + }); +} + +function blankGridLines(count = 2) { + return Array.from({ length: count }, () => blankGridLine()); +} + function tableXml(headers, rows) { const colCount = headers.length; const colWidth = Math.floor(CONTENT_W / colCount); @@ -466,10 +569,21 @@ function isMatchingParagraph(block, text) { } function isChineseDateParagraph(block) { - return ( - block?.type === "paragraph" && - /^\d{4}年\d{1,2}月\d{1,2}日$/.test(normalizeSignatureText(block.text)) - ); + if (block?.type !== "paragraph") return false; + try { + normalizeChineseDate(normalizeSignatureText(block.text), "source date"); + return true; + } catch { + return false; + } +} + +function validateDateParagraphs(blocks) { + for (const block of blocks) { + if (block?.type === "paragraph" && /^\d{4}年\d{1,2}月\d{1,2}日$/.test(normalizeSignatureText(block.text))) { + normalizeChineseDate(normalizeSignatureText(block.text), "source date"); + } + } } function stripTrailingMatchingSignature(blocks, args) { @@ -556,6 +670,7 @@ function buildDocument(blocks, args) { const letterhead = args.letterhead === "digital" ? "digital" : "preprinted"; const pageNumberMode = resolvePageNumberMode(args, format); const body = []; + let titleGapLines = 0; const pageMarginTop = format === "letter" ? 1701 : format === "command" ? 1134 : 2098; const sourceBlocks = [...blocks]; if (!title && sourceBlocks[0]?.type === "heading") { @@ -564,6 +679,7 @@ function buildDocument(blocks, args) { if (title && sourceBlocks[0]?.type === "heading" && sourceBlocks[0].text.trim() === title.trim()) { sourceBlocks.shift(); } + validateDateParagraphs(sourceBlocks); stripTrailingMatchingSignature(sourceBlocks, args); let mainSend = args.to; if (mainSend && isMatchingParagraph(sourceBlocks[0], mainSend)) sourceBlocks.shift(); @@ -580,30 +696,49 @@ function buildDocument(blocks, args) { if (args["copy-no"]) body.push(headerFieldParagraph(String(args["copy-no"]).padStart(6, "0"), "body")); if (args.secret) body.push(headerFieldParagraph(args.secret)); if (args.urgent) body.push(headerFieldParagraph(args.urgent)); - if (letterhead === "digital" && args.org) body.push(agencyMarkParagraph(args.org, String(Math.max(0, 1984 - headerFieldCount * 560)))); + if (letterhead === "digital" && args.org) { + body.push(agencyMarkParagraph(args.org, String(Math.max(0, 1984 - headerFieldCount * 560)), "0")); + if (args["doc-no"]) body.push(...blankGridLines(2)); + } if (letterhead === "preprinted") { const reserve = Number(args["letterhead-reserve-mm"] ?? 72); if (!Number.isFinite(reserve) || reserve < 37 || reserve > 130) throw new Error("--letterhead-reserve-mm must be between 37 and 130"); body.push(letterheadReserveParagraph(Math.max(37, reserve - headerFieldCount * 9.877))); } + if (args["doc-no"]) args["doc-no"] = normalizeDocumentNumber(args["doc-no"]); if (upward && args["doc-no"] && args.signer) body.push(upwardHeaderParagraph(args["doc-no"], args.signer)); else if (args["doc-no"]) body.push(documentNumberParagraph(args["doc-no"], upward)); if (letterhead === "digital" || args["preprinted-rule"] === "true") body.push(redRule()); + titleGapLines = 2; } else if (format === "letter") { if (!args.org) throw new Error("letter format requires --org"); body.push(paragraph(args.org, { align: "center", indent: false, fontPreset: "title", size: "44", color: "FF0000", before: "0", after: "227" })); body.push(redDoubleRule({ upperMm: "0.35", lowerMm: "0.25" })); - if (args["doc-no"]) body.push(headerFieldParagraph(args["doc-no"], "body", { align: "right" })); + if (args["doc-no"]) { + args["doc-no"] = normalizeDocumentNumber(args["doc-no"]); + body.push(headerFieldParagraph(args["doc-no"], "body", { align: "right", before: "280" })); + } + titleGapLines = 2; } else if (format === "command") { if (!args.org) throw new Error("command format requires --org"); - body.push(paragraph(args.org, { align: "center", indent: false, fontPreset: "title", size: "44", color: "FF0000", before: "0", after: "1120" })); - if (args["doc-no"]) body.push(paragraph(args["doc-no"], { align: "center", indent: false, fontPreset: "body", size: "32", after: "1120" })); + if (!args["doc-no"]) throw new Error("command format requires --doc-no for the 令号"); + body.push(paragraph(args.org, { align: "center", indent: false, fontPreset: "title", size: "44", color: "FF0000", before: "0", after: "0" })); + if (args["doc-no"]) { + body.push(...blankGridLines(2)); + body.push(paragraph(args["doc-no"], { align: "center", indent: false, fontPreset: "body", size: "32", after: "0" })); + body.push(...blankGridLines(2)); + } } else if (format === "minutes") { if (!args.org) throw new Error("minutes format requires --org (for XXXXX纪要)"); + if (!/纪要$/.test(String(args.org).trim())) throw new Error("minutes --org must end with“纪要”"); body.push(paragraph(args.org, { align: "center", indent: false, fontPreset: "title", size: "44", color: "FF0000", before: "1984", after: "560" })); } - const hasRedRule = (formal && (letterhead === "digital" || letterhead === "preprinted" || args["preprinted-rule"] === "true")) || format === "letter"; - if (title) body.push(titleParagraph(title, hasRedRule ? "1120" : "0")); + if (args.date) args.date = normalizeChineseDate(args.date); + if (args["print-date"]) args["print-date"] = normalizeChineseDate(args["print-date"], "--print-date", true); + if (title) { + if (titleGapLines) body.push(...blankGridLines(titleGapLines)); + body.push(titleParagraph(title)); + } if (args.subtitle) body.push(subtitleParagraph(args.subtitle)); if (mainSend) body.push(mainSendParagraph(mainSend)); @@ -619,20 +754,13 @@ function buildDocument(blocks, args) { } body.push(renderParagraph(block)); } - if (args["attachment-note"]) body.push(attachmentNote(args["attachment-note"])); + if (args["attachment-note"]) { + if (body.at(-1) === emptyLine()) body.pop(); + body.push(attachmentNote(args["attachment-note"])); + } if (args.sender || args.date) { body.push(emptyLine()); - if (args["seal-mode"] === "signed") { - if (!args.signer || !args["signer-title"]) throw new Error("signed seal mode requires --signer and --signer-title"); - body.push(right(`${args["signer-title"]} ${args.signer}`)); - if (args.date) body.push(right(args.date)); - } else if (args["seal-mode"] === "seal") { - if (args.sender) body.push(right(args.sender)); - if (args.date) body.push(right(args.date)); - } else { - if (args.sender) body.push(right(args.sender)); - if (args.date) body.push(right(args.date)); - } + body.push(...signatureParagraphs(args)); } if (args.note) body.push(paragraph(`(${args.note.replace(/^(|)$/g, "")})`, { align: "left", fontPreset: "body", size: "32" })); const attachmentFiles = Array.isArray(args["attachment-file"]) ? args["attachment-file"] : args["attachment-file"] ? [args["attachment-file"]] : []; @@ -652,7 +780,7 @@ function buildDocument(blocks, args) { : pageNumberMode === "center" ? '' : ""); - const footerDistance = format === "letter" ? "1134" : "1588"; + const footerDistance = format === "letter" || pageNumberMode === "standard" ? "1134" : "1588"; body.push(`${footerRefs}`); return `${body.join("")}`; } diff --git a/scripts/verify_gongwen_docx.mjs b/scripts/verify_gongwen_docx.mjs index 50b05a0..72e4c51 100755 --- a/scripts/verify_gongwen_docx.mjs +++ b/scripts/verify_gongwen_docx.mjs @@ -101,16 +101,26 @@ const titleFontStandard = titleParagraphs.every((p) => /w:eastAsia="(?:方正小 const titleFontAvailable = titleFontStandard || (!requireStandardFonts && titleParagraphs.every((p) => /w:eastAsia="Songti SC"/.test(p))); check("Title style and font", titleFontAvailable, requireStandardFonts ? "small-standard-title font is required" : titleParagraphs.length ? `${titleParagraphs.length} title paragraph(s); Songti SC fallback is allowed unless strict mode is enabled` : "not present in this document"); +const isExplicitBlankGridLine = (p) => /w:spacing w:before="0" w:after="0" w:line="560" w:lineRule="exact"/.test(p) && /w:sz w:val="2"/.test(p) && /]*> <\/w:t>/.test(p); +const firstTitleIndex = paragraphs(doc).findIndex((p) => /w:pStyle w:val="GongwenTitle"/.test(p)); +const titlePrecedingParagraphs = firstTitleIndex < 0 ? [] : paragraphs(doc).slice(0, firstTitleIndex); +const titleGapParagraphs = titlePrecedingParagraphs.slice(-2); +const hasExplicitTwoLineTitleGap = titleGapParagraphs.length === 2 && titleGapParagraphs.every(isExplicitBlankGridLine); +check("Title has two explicit blank grid lines", !["formal", "letter"].includes(profile) || hasExplicitTwoLineTitleGap, "title is preceded by two exact 28-point blank paragraphs"); +if (/附件:/.test(textOf(doc))) check("Attachment-note continuation indent", /w:firstLine="-960"[^>]*w:left="1600"/.test(doc), "附件说明回行与名称首字对齐"); + if (profile === "ordinary") { check("No formal red-head drawing", !/w:color w:val="FF0000"/.test(doc), "ordinary output has no red head or red rule"); check("Centered layout footer", /w:jc w:val="center"/.test(footerCenter) && /w:instr="PAGE"/.test(footerCenter), "ordinary output uses centered page number"); } else if (profile === "letter") { check("Letter first-page page number", !footerCenter && !footerOdd && !footerEven && !/w:instr="PAGE"/.test(footerLetter), "letter output suppresses page numbers"); check("Letter upper red double rule", (footerLetter.match(/w:fill="FF0000"/g) ?? []).length >= 2 && /w:tblW[^>]*w:w="9638"/.test(doc), "letter output contains 170 mm red double rules"); + check("Letter upper rule distance", /w:after="227"/.test(paragraphs(doc)[0] ?? ""), "top red double rule is 4 mm below the agency mark"); check("Letter bottom rule footer", /rIdLetterFooter/.test(doc) && /w:footer="1134"/.test(doc), "bottom red double rule is anchored 20 mm from the paper edge"); } else { check("Odd/even page fields", /w:instr="PAGE"/.test(footerOdd) && /w:instr="PAGE"/.test(footerEven), "odd and even page footers exist"); check("Odd/even page positions", /w:jc w:val="right"/.test(footerOdd) && /w:jc w:val="left"/.test(footerEven), "odd right and even left"); + check("Formal page-number distance", /w:footer="1134"/.test(doc), "page-number footer is 7 mm below the 225 mm type area"); } if (profile === "formal") { if (args.letterhead === "digital") { @@ -122,22 +132,37 @@ if (profile === "formal") { const headerFieldCount = agencyIndex < 0 ? 0 : docParagraphs.slice(0, agencyIndex).filter((p) => /\d{6}|机密|秘密|特急|加急/.test(textOf(p))).length; const expectedAgencyBefore = 1984 - headerFieldCount * 560; check("Digital red-head agency position", new RegExp(`w:before="${expectedAgencyBefore}"`).test(doc), `agency mark starts 35 mm below the type-area top after ${headerFieldCount} header field line(s)`); - check("Two blank lines below red rule", titleParagraphs.some((p) => /w:before="1120"/.test(p)), "title paragraph reserves two 28-point grid lines below the red rule"); + const docNoIndex = docParagraphs.findIndex((p) => /〔\d{4}〕[1-9]\d*号/.test(textOf(p))); + const agencyToDocNo = agencyIndex >= 0 && docNoIndex > agencyIndex ? docParagraphs.slice(agencyIndex + 1, docNoIndex) : []; + check("Agency mark to document number two blank lines", docNoIndex < 0 || (agencyToDocNo.length === 2 && agencyToDocNo.every(isExplicitBlankGridLine)), "document number is two exact 28-point grid lines below the agency mark"); + check("Two blank lines below red rule", hasExplicitTwoLineTitleGap, "title is preceded by two exact 28-point blank paragraphs below the red rule"); + if (docNoIndex >= 0) check("Document number syntax", /〔\d{4}〕[1-9]\d*号/.test(textOf(docParagraphs[docNoIndex])) && !/第/.test(textOf(docParagraphs[docNoIndex])), "year uses full digits, sequence has no 第 or leading zero, and ends with 号"); if (args.upward === "true") { check("Upward document number and signer share one row", paragraphs(doc).some((p) => /签发人:/.test(textOf(p)) && /〔|\[|文号|发/.test(textOf(p))), "signer is in the same paragraph row as the document number"); } } else { check("Preprinted letterhead reserve", /w:before="\d{4,}"/.test(doc), "first-page top reserve is present"); - check("No red drawing for preprinted letterhead", !/w:color w:val="FF0000"/.test(doc), "preprinted mode does not redraw red letterhead or rule"); - check("Title spacing below physical red rule", titleParagraphs.some((p) => /w:before="1120"/.test(p)), "title paragraph reserves two 28-point grid lines below the printed red rule"); + const preprintedParagraphs = paragraphs(doc); + const titleXmlStart = firstTitleIndex < 0 ? doc.length : doc.indexOf(preprintedParagraphs[firstTitleIndex]); + const preprintedHeaderXml = titleXmlStart < 0 ? doc : doc.slice(0, titleXmlStart); + check("No red drawing for preprinted letterhead", !/w:color w:val="FF0000"/.test(preprintedHeaderXml), "preprinted mode does not redraw red letterhead or rule"); + check("Title spacing below physical red rule", hasExplicitTwoLineTitleGap, "title is preceded by two exact 28-point blank paragraphs below the printed red rule"); } } if (profile === "letter") { - check("Letter title spacing", titleParagraphs.some((p) => /w:before="1120"/.test(p)), "title paragraph reserves two 28-point grid lines below the red rule"); + check("Letter title spacing", hasExplicitTwoLineTitleGap, "title is preceded by two exact 28-point blank paragraphs below the red rule"); + const letterDocNo = paragraphs(doc).find((p) => /〔\d{4}〕[1-9]\d*号/.test(textOf(p))); + if (letterDocNo) check("Letter document number distance", /w:before="280"/.test(letterDocNo), "document number is 3号汉字高度7/8 below the first red double line"); } if (profile === "command") { - check("Command agency-to-order spacing", /w:after="1120"/.test(paragraphs(doc)[0] ?? ""), "command agency mark is followed by two blank lines"); - check("Command order-to-body spacing", paragraphs(doc).slice(1).some((p) => /w:after="1120"/.test(p) && /w:jc w:val="center"/.test(p)), "command number is followed by two blank lines"); + const commandParagraphs = paragraphs(doc); + const commandAgencyIndex = commandParagraphs.findIndex((p) => /w:color w:val="FF0000"/.test(p)); + const commandOrderIndex = commandParagraphs.findIndex((p, index) => index > commandAgencyIndex && /w:jc w:val="center"/.test(p) && /w:sz w:val="32"/.test(p) && !/w:color w:val="FF0000"/.test(p)); + const agencyToOrder = commandAgencyIndex >= 0 && commandOrderIndex > commandAgencyIndex ? commandParagraphs.slice(commandAgencyIndex + 1, commandOrderIndex) : []; + check("Command agency-to-order spacing", commandOrderIndex < 0 || (agencyToOrder.length === 2 && agencyToOrder.every(isExplicitBlankGridLine)), "command agency mark is followed by two exact 28-point grid lines"); + const commandTitleIndex = firstTitleIndex; + const orderToBody = commandOrderIndex >= 0 && commandTitleIndex > commandOrderIndex ? commandParagraphs.slice(commandOrderIndex + 1, commandTitleIndex) : []; + check("Command order-to-body spacing", commandTitleIndex < 0 || (orderToBody.length === 2 && orderToBody.every(isExplicitBlankGridLine)), "command number is followed by two exact 28-point grid lines"); } if (profile === "minutes") { const attendance = paragraphs(doc).filter((p) => /(?:出席|请假|列席):/.test(textOf(p))); @@ -148,6 +173,8 @@ const redRules = doc.match(/height:(?:0\.35|0\.25|0\.5)mm/g) ?? []; if (["formal", "command", "minutes"].includes(profile)) { const hasColophon = /抄送:|印发/.test(textOf(doc)); check("Colophon line thickness model", !hasColophon || (redRules.includes("height:0.35mm") && redRules.includes("height:0.25mm")), "版记粗线0.35 mm、细线0.25 mm"); + if (hasColophon) check("Colophon bottom anchor", /w:vertAnchor="margin"[^>]*w:horzAnchor="margin"[^>]*w:tblpYSpec="bottom"/.test(doc), "版记浮动表锚定在最后一页版心底部且禁止重叠"); + if (/抄送:/.test(textOf(doc))) check("Colophon continuation indent", /w:firstLine="-960"[^>]*w:left="1280"/.test(doc), "抄送回行与冒号后的首字对齐"); if (hasColophon) check("Colophon print-row right tab", /w:tab w:val="right"/.test(doc), "印发机关和日期使用右制表位"); } diff --git a/tests/run_tests.sh b/tests/run_tests.sh index f295a9c..08d1e80 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -24,7 +24,7 @@ unzip -p "$TMP_DIR/layout.docx" word/document.xml | grep -q 'w:pStyle w:val="Hea "$NODE" "$VERIFY" --input "$TMP_DIR/standard.docx" --profile formal --letterhead preprinted ! unzip -p "$TMP_DIR/standard.docx" word/document.xml | grep -q '示例单位文件' unzip -p "$TMP_DIR/standard.docx" word/document.xml | grep -q '示例发〔2026〕1号' -unzip -p "$TMP_DIR/standard.docx" word/document.xml | grep -q 'w:before="1120"' +unzip -p "$TMP_DIR/standard.docx" word/document.xml | grep -q 'w:snapToGrid w:val="false"' "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/digital.docx" --format formal --letterhead digital --org "示例单位文件" --doc-no "示例发〔2026〕1号" --title "电子红头测试" --page-number standard "$NODE" "$VERIFY" --input "$TMP_DIR/digital.docx" --profile formal --letterhead digital @@ -32,7 +32,7 @@ unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q '示例单位文件 unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'FF0000' unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'height:0.5mm' unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'w:before="1984"' -unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'w:before="1120"' +unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'w:snapToGrid w:val="false"' cat >"$TMP_DIR/attachment.md" <<'EOF' # 附件一:测试附件 @@ -48,10 +48,31 @@ unzip -p "$TMP_DIR/upward.docx" word/document.xml | grep -q '签发人:' unzip -l "$TMP_DIR/letter.docx" | grep -q 'word/footerLetter.xml' unzip -p "$TMP_DIR/letter.docx" word/footerLetter.xml | grep -q 'w:fill="FF0000"' -"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/with-attachment.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔2026〕1号" --title "附件测试" --attachment-note "附件一:测试附件" --attachment-file "$TMP_DIR/attachment.md" -unzip -p "$TMP_DIR/with-attachment.docx" word/document.xml | grep -q '附件一:测试附件' +"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/with-attachment.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔2026〕1号" --title "附件测试" --attachment-note "1. 测试附件" --attachment-file "$TMP_DIR/attachment.md" +unzip -p "$TMP_DIR/with-attachment.docx" word/document.xml | grep -q '附件:1. 测试附件' unzip -p "$TMP_DIR/with-attachment.docx" word/document.xml | grep -q 'w:pageBreakBefore' +"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/command.docx" --format command --org "示例机关命令" --doc-no "第1号" --title "命令正文" +"$NODE" "$VERIFY" --input "$TMP_DIR/command.docx" --profile command + +"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/minutes.docx" --format minutes --org "示例机关纪要" --title "纪要正文" --attendees "张三、李四" --absent "王五" +"$NODE" "$VERIFY" --input "$TMP_DIR/minutes.docx" --profile minutes + +"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/seal-layout.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --title "签章位置测试" --sender "示例机关" --date "2026年9月10日" --seal-mode seal +unzip -p "$TMP_DIR/seal-layout.docx" word/document.xml | grep -q 'w:right="1280"' + +if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/bad-doc-no.docx" --format formal --letterhead digital --org "示例单位文件" --doc-no "示例发〔2026〕01号" --title "非法文号" >"$TMP_DIR/bad-doc-no.out" 2>&1; then + echo "invalid document number unexpectedly succeeded" >&2 + exit 1 +fi +grep -q 'GENERATOR ERROR:' "$TMP_DIR/bad-doc-no.out" + +if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/bad-date.docx" --format formal --letterhead preprinted --date "2026年09月10日" --title "非法日期" >"$TMP_DIR/bad-date.out" 2>&1; then + echo "invalid date unexpectedly succeeded" >&2 + exit 1 +fi +grep -q 'GENERATOR ERROR:' "$TMP_DIR/bad-date.out" + if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/strict.docx" --format formal --letterhead digital --org "示例单位文件" --title "严格字体测试" --require-standard-fonts >"$TMP_DIR/strict.out" 2>&1; then if ! fc-list -f '%{family}\n' | grep -Eq '方正小标宋简体|方正小标宋_GBK|FZXiaoBiaoSong'; then echo "strict font mode unexpectedly succeeded without a small-standard-title font" >&2