Files
ouc_latex_template/特殊事项说明表
laoyang 1abb3cf5ef 特殊事项说明表示例文案换为浮标维护场景 #16
原 \sxdesc 示例是「去菏泽做项目研讨与系统演示、自驾」,用户要求换掉。
改为「渤海湾生态环境监测浮标例行维护,前往昌邑近岸观测站更换传感器
并回收监测数据,该站位于滩涂养殖区公共交通无法抵达,需随车携带
备件箱、采样冷藏箱与工具,故自驾前往」。

选这段的理由:保持原示例「为什么必须自驾」的两条必要性(公共交通不便 +
携带专用设备),只把事由换成本课题范畴内的真实场景,示范作用不变。

影响面:仅 \sxdesc 一个宏,未动任何排版代码。

验证:
- xelatex 0 error / 0 Overfull / 0 Underfull / 1 页
- 四条横线 y = 172.99 / 457.49 / 516.34 / 573.64,与原件逐点一致
- 描述仍为 2 行(227.81 / 241.49),签名区、备注区、页码坐标全部未变
  说明 \vfil 正确吸收了文案长度变化,这正是本模板相对原 docx
  「8 个手写空段」的核心改进
2026-10-28 19:50:37 +08:00
..

特殊事项说明表 — 中国海洋大学 特殊事项说明表(LaTeX 版)

把报销用的《中国海洋大学特殊事项说明表》Word 版复刻为 LaTeX 模板。 版式与原 .docx 逐点对齐(见文末「实测偏差」), 不含任何真人信息:事项描述是虚构的报销事由,三处日期留空待手写。

文件

文件 说明
特殊事项说明表.tex 主文件。填表只改第 1 节(4 个变量)
ouccsx.sty 版式包。页面/字体/行高/全部尺寸写在这里,附换算注释

编译

xelatex 特殊事项说明表.tex

单页,跑一遍即可(无交叉引用)。

需要 XeLaTeX,并安装 ctex、geometry、fontspec、xeCJK、fancyhdr。 实测环境:TeX Live 2022。

改内容

\newcommand{\sxdesc}{%           % 事项描述(必填)
因项目研讨需要,前往菏泽进行项目研讨与系统演示,因携带大件演示设备,
公共交通多有不便,因此采用自驾方式前往。}

\newcommand{\sxdateA}{}          % 项目负责人签字处日期
\newcommand{\sxdateB}{}          % 单位负责人签字处日期
\newcommand{\sxdateC}{}          % 科技管理部门签字处日期

就这四个。表内其余文字全是学校规定原文,不要改 —— 「兹证明该事项真实有效,本人愿意为此承担责任。」「学院对本单位科研 经费使用承担监督和管理责任。」以及备注三条,都在 ouccsx.sty 里 装配好了,改了就与原件不一致。

关于 \sxdesc

描述可以写多段,空行或 \\ 分段都行,表格会自动让出空间:

  • 写短 → 空白落在「描述区」与「签名区」之间,签名区位置不动
  • 写长 → 空白被吃掉,签名区仍然不动

这是本模板比原 docx 更稳的地方。原件靠 R0 里的 8 个空段 给手写 留白,描述一写长就把签名区顶到下一行(Word 里也一样)。本模板删掉 那 8 个空段,改成「上下两半 + 中间 \vfil」,两头都不破版。

⚠️ 描述特别长时签名区会挤出行外 —— 这是内容超量的正常表现(Word 里 写太多同样会把格子撑高),把描述删短即可,不是 bug。

关于三处日期

留空则印成「  年   月   日」,签字时手写。 想直接打印就填年份:\newcommand{\sxdateA}{2026} 会印在「年」前面。

默认留空的理由同 成绩构成说明:印了年份就占掉手写空间, 而且报销常常是事后补开,日期往往就是签字那天。

版式数据来源

全部尺寸从原 .docx 的 OOXML 里量出来,换算关系:

  • 长度:mm = twips ÷ 1440 × 25.4,pt = twips ÷ 20
  • 字号:w:sz 是半磅值,sz=21 ⇒ 10.5 pt
  • 行距:w:line 是 1/20 pt,lineRule="exact" 时即精确值
项目 docx 原值 换算
纸张 pgSz 11906×16838 A4 210×297 mm
页边距(上下) pgMar top/bottom 2155/1985 38.01 / 35.00 mm
页边距(左右) pgMar left/right 1588/1474 28.01 / 26.00 mm
表宽 tblW 9613 169.54 mm = 480.65 bp
表左缩进 tblInd −546 −9.63 mm(把表顶出版心)
单元格内边距 tblCellMar 108 5.4 pt(左右),上下为 0
边框 tblBorders sz=4 0.5 pt
字号 标题 sz44/正文 sz21/签字日期 sz24/页码 sz28 22 / 10.5 / 12 / 14 pt
行距 标题 line500/备注 line300/表内未设 25 / 15 pt/Word 单倍(字号×1.3)

表格结构:单列 3 行(tblGrid 只有一个 gridCol,每行一个 tc), 即三行整宽方框上下叠。没有合并单元格、没有多列,所以没用 tabularray —— 手工 \vbox 叠三块反而更可控(行高精确、内线不重复、内容可用 \vfil 撑)。

行高为什么用实测值而不是 trHeight

trHeight 是 5091 / 1140 / 634 twips,hRule="atLeast"。 atLeast 是「最小」,实际高度由内容撑开:

行 trHeight 实际被内容撑到 段落数
R0 事项描述 254.55 pt 284.00 bp 19
R1 单位负责人 57 pt 58.35 bp 4
R2 科技管理 31.70 pt 56.80 bp 4

照 trHeight 会让整表矮 40 bp。故取实测撑开后的高度。

六个必须知道的坑

这一节的每一条都是实测踩出来的,改 ouccsx.sty 之前务必看完。

1. 版心要取「表格宽度」,不是 docx 版心

原表宽 169.54 mm,比 docx 版心(156 mm)宽 13.54 mm, 靠 tblInd = −546 twips 向左顶出版心。

若把 geometry 设成 docx 版心(left = 28.01mm),表格必然超出版心 13.54 mm,报一条 Overfull \hbox,而且改内容时位置会漂。

⇒ 本包把版心直接设成表格宽度:left = 16.47mm、totalwidth = 169.54mm。 表格正好填满版心,零 Overfull,与原件逐点相同。

代价:标题若用 \centering 会落在 287.03 bp,而原件在 300.41 bp(居中于 docx 版心),偏左 13.4 bp。故标题另给 \sxtitleindent + \sxtitlewidth 把中心校回去 —— 不要把标题改成 \centering。

2. \baselineskip 管不了本表的行距

本表行距逐行不同(15.24 / 15.48 / 14.04 / 13.56 / 13.68 bp), 因为 10.5 pt 与 12 pt 的字混排,Word 按「下一行字号 × 1.3」算。

而 \baselineskip 只在同一段落内部管行距。一旦每行 \par 自成一段, 段与段之间是另一套规则:

段间基线距 = 上段末行深度 + \parskip + 本段首行字高

这里 \parskip = 0、首行字高由字体 ascent 定死,\baselineskip 根本没被读到。 实测第一版所有行距被压成 13.51,12 pt 的签名行也没变高,整表矮 2.25 bp。

⇒ 正解:不依赖 \baselineskip,逐行显式插胶:

基线距 = 上盒深度 + \kern + 本盒高度
⇒ \kern = 目标距 − 上盒深度 − 本盒高度

这就是 \sx@bl / \sx@blp 的全部内容。与字体度量彻底解耦:换字体、 换字号、描述写长写短,行距一个 bp 都不变。

唯一的例外是「段内折行的长句」—— 那是一个段落里的多行,规则不同, \baselineskip 在此有效,故 \sx@blp 仍给它。

3. 块首行必须用 \kern,不能用 \vskip

TeX 会丢弃 \vbox 开头的胶。用 \vskip 给的顶距会静默消失 (实测「事项描述:」给 10.53 只落到 5.38,等于只剩字高)。 \kern 不受这条约束,且照样计入盒高。

同理,块末尾不能用 \vskip 补空白(胶也被丢弃), 要用 \sx@bl 的可选参数 #1 直接撑末盒的深度。

4. 负的 \kern 不能被挡

相邻两行的目标距可能小于「上盒深度 + 本盒高度」。例:年月日行是 12 pt 字, 目标距 14.04,而 2.99 + 12.06 = 15.05,差值是 −1.01。

最初加了 \ifdim\sx@tmp>\z@ 守卫,负值被静默吞掉 → 该行退回自然距 15.05, 整块错位 10 bp。负 \kern 在 TeX 里完全合法(盒间轻微重叠),不该挡。

5. 多行段落:\ht 参照的是末行基线

\vbox 的 \ht = 首行高度 + (n−1)·行距,它的参考点是末行基线。

所以「上一行基线 → 本段首行基线」的距离不是 pd + kern + ht, 而是 pd + kern + ht − (n−1)·行距。漏掉这一项,多行段自己被推下一整行, 其后所有行跟着错(实测 0.48 而非 14.04)。

⇒ 用 \prevgraf 取段内行数,把 (n−1)·行距 加回 kern。\dp 不用改 —— \vbox 的 \dp 就是末行的真实深度。

⚠️⚠️ \prevgraf 有两道坎,两个都踩过:

  • 必须在 \vbox 内部取 —— 盒一关闭就恢复成外层值(实测把 2 读成 0)
  • 必须在 \par 之后取 —— 它是在段落断行那一步才被写入的(\par 前读到仍为 0)

6. \vbox to N 设的是高度,深度另计

若最后一项是带深度的 \box,\vbox to N 的 \dp 就是那个深度, 总高变成 N + 深度。本表末行深 3.11,于是 R0 变成 287.11 而非 284.00, 四条横线全部下移。

⇒ 末尾补一条 \vskip\z@:让末项变成胶(胶无深度),\dp 归零, 总高才正好等于 N。见 \sx@cellvf。

与原件的有意差异

项 docx 本模板 说明
字体 宋体 SimSun OUC Serif(思源宋体子集) 原用系统字体。改仓库公共字体包 ../fonts/,换机器/换系统都能编,且允许分发
标题字体 方正小标宋简体 OUC Serif 真粗体 小标宋是商业字体,本库不分发。按 课程执行大纲/ 的既定做法换真粗体:小标宋本就是宋体的标题变体,真粗体比伪粗更接近,笔画不糊
第 3 条备注编号 手打的「   3.  」,无 numPr 与前两条统一排齐 唯一的位置差异:原件该条比前两条偏 0.36 bp。原件是漏配自动编号后手打的,属笔误;本模板统一对齐
R0 描述区留白 8 个空段 上下两半 + 中间 \vfil 见上文「关于 \sxdesc」。描述长短不再影响签名区位置

除此之外没有任何版式差异。

实测偏差

不用肉眼看 PDF,直接用 pymupdf 取矢量坐标,与 Word 2007 导出的 PDF 逐项相减:

span['origin']        # 真实基线坐标(bbox 顶端受字形上伸部影响,误差约 2 bp)
page.get_drawings()   # 每条线的精确端点

结果(33 个文字元素 + 6 条线,dY/dX 为「本模板 − 原件」,单位 bp):

元素 dY dX
标题 0.00 −0.01
四条表格横线 0.00 —
事项描述 / 描述首行 / 次行 0.00 +0.03
兹证明 0.00 +0.04
项目负责人(签名) 0.00 +0.03
年月日(三处,各 3 字) 0.00 +0.03
项目负责人是 / 责任。/ 非科研经费 0.00 +0.03
本人证明 / 单位负责人 / 单位公章 0.00 +0.03
学院对本单位 0.00 +0.03
科技管理 / 负责人签字 / 单位公章 0.00 +0.03
备注四条 −0.01 −0.42 ~ −0.82
页码 0.00 0.00

33 个文字元素的 dY 全部 0.00(< 0.005 mm),dX ≤ 0.04 bp, 唯备注区 dX 偏 0.420.82 bp(约 0.150.29 mm,肉眼不可辨)—— 那是 OUC Serif 与 SimSun 的字宽差累积,不是定位误差。

像素级差分(150 dpi)总差异 3.03%,且每条差异带都精确落在有文字的行上, 没有整体错位的偏移带 —— 3% 全是字形轮廓不同造成的。

校验方法

量矢量而不是量像素:

pdftocairo -svg 特殊事项说明表.pdf out.svg    # 线条精确坐标
pdftotext -bbox 特殊事项说明表.pdf out.xml    # 字块坐标

或用 pymupdf(本模板就是这么核的):page.get_drawings() 取线, get_text('rawdict') 取 span['origin'] 作基线。

⚠️ 注意 bp 与 pt 的不同:PDF 用户单位是 bp(1/72 in), LaTeX 的 pt 是 1/72.27 in,差 0.375%。A4 上累积到 2 pt, 超过一个字符宽。本包凡与 PDF 对齐的几何量一律用 bp。