七个模板原先分两套做法:幻灯片/fonts(49MB 鸿蒙 18 个字重)与 科技报告/font(92MB,含 5 款方正商业字体),其余模板直接调系统 SimSun/SimHei/KaiTi/FangSong/Times New Roman。换机器就编不出来, 方正系列也不允许随仓库分发。 统一到仓库根的 fonts/ + fonts.tex: - OUC Serif(思源宋体 SC 子集) —— 宋体 - OUC Sans(思源黑体 SC 子集) —— 黑体 - OUC Kai(霞鹜文楷子集) —— 楷体 - Zhuque Fangsong(朱雀仿宋) —— 仿宋 - TeX Gyre Termes —— 西文 Times 等价(度量逐字相同) - HarmonyOS Sans —— 幻灯片/科技报告原有字体 fonts.tex 是唯一允许声明字体的地方,7 个模板全部 \input 它。 字体体积 141MB -> 49MB(去掉重复),全库共用一套最小集合。 几处踩过的坑(均已写进源码注释): - \newCJKfontfamily 与 ctex 默认 fontset 冲突会中断编译,改用 \setCJKfamilyfont + \def - \newfontfamily 缺粗体档会让 \bfseries 静默失效,四档必须全给 - UprightFont= 与主参数不能同时给,否则文件名被拼两次 - Path= 相对编译时工作目录,不是相对 .sty - ×/□/○ 等符号按 Unicode 区段被判成西文,需 \xeCJKDeclareCharClass - 中文斜体的 ItalicFont 与主字体不在同一目录,须在 ItalicFeatures 里单独写 Path/Extension(否则报 OUCKai-Regular cannot be found) 顺带: - 科技报告 report.tex 的字体演示正文改为新字体名(原文写着方正系列) - 试卷归档封面新模板并入本库 - 科技报告 shell 命令框底色改海大蓝(#5 遗留) 验证(7 个模板 8 个 PDF 全量重编 + 逐行 A/B 对照): - 错误 0,缺字 0 - 幻灯片 362 行逐行完全一致,零漂移 - 年度考核登记表 / 劳务发放说明表 / 试卷题型示例 / 参考答案 文本完全一致 - 课程执行大纲 仅 2 处断字连字符差异(throughout、processing), 1 条 6.39pt Overfull 已论证不可见(距纸张右边缘仍有 20.7mm) - 科技报告 内嵌字体方正归零,只剩 OUC Serif/Sans/Kai + Zhuque - 全库正文 857 个非 ASCII 字符,四款中文字体(含粗体)覆盖 100%
劳务发放说明表 — 中国海洋大学 劳务发放说明表(LaTeX 版)
把实验室用的《劳务发放说明表》Word 版复刻为 LaTeX 模板。版式与原 .docx
逐项对齐,不含任何真人信息:姓名、人数、项目名称、单位名称一律 × 占位。
文件
| 文件 | 说明 |
|---|---|
劳务发放说明表.tex |
主文件。填表只改第 1 节(4 个变量) |
ouclw.sty |
版式包。页面/字体/列宽/行高/全部尺寸写在这里,附换算注释 |
编译
xelatex 劳务发放说明表.tex
单页,跑一遍即可(无交叉引用)。
需要 XeLaTeX,并安装 ctex、geometry、fontspec、xeCJK、tabularray。
实测环境:TeX Live 2022 + tabularray 2022A。
改内容
\newcommand{\lwunit}{{\songti ××××××××}} % 用工单位(学部 / 学院)
\newcommand{\lwreason}{{\songti ××××}} % 用工事由
\newcommand{\lwnote}{} % 备注(可留空)
\newcommand{\lwwork}{%
聘请{\songti ×××}等{\songti ×}位研究生参与《{\songti ×××}》项目的研究工作。}
关于 \lwwork 的句式
原文是「聘请秦依康等 2 位研究生参与《观测数据驱动的渤海生态系统健康评估
关键技术研发》项目的研究工作。」——真人姓名、人数、真实项目名已替换为
××× / ×,句式一字未动。填表时把三处 × 换成实际内容即可:
| 占位 | 换成 |
|---|---|
第一个 ××× |
第一位研究生的姓名("等"字后面若只有一人,可删"等×位") |
×(位) |
研究生人数 |
《×××》 |
项目全称 |
版式数据来源
全部尺寸从原 .docx 的 OOXML 里量出来,换算关系:
- 长度:
mm = twips ÷ 1440 × 25.4,pt = twips ÷ 20 - 字号:
w:sz是半磅值,sz=28⇒ 14 pt - 行距:
docGrid linePitch=312twips ⇒ 15.6 pt
| 项目 | docx 原值 | 换算 |
|---|---|---|
| 纸张 | pgSz 11906×16838 |
A4 210×297 mm |
| 页边距(上下) | pgMar top/bottom 1361 |
24.01 mm |
| 页边距(左右) | pgMar left/right 1797 |
31.70 mm |
| 表宽 | tblW 8522 |
150.32 mm |
| 列宽 c1..c4 | 1384, 1418, 2883, 2837 |
24.41 / 25.01 / 50.85 / 50.04 mm |
| 单元格内边距(左右) | tblCellMar 108 |
1.905 mm |
| 边框 | tblBorders sz=4 |
0.5 pt |
| 字号 | w:sz 28 |
14 pt,宋体 |
| 行距 | linePitch 312 |
15.6 pt |
各行行高由段落数决定(原表没有 trHeight),每段一个行距 15.6 pt:
| 行 | 段数 | 高度 |
|---|---|---|
| R0 用工单位 | 2(用工单位 / (公章)) |
31.2 pt = 11.01 mm |
| R1 用工事由 | 1 | 15.6 pt = 5.50 mm |
| R2 具体工作 | 9(长句 + 8 空段) | 140.4 pt = 49.53 mm |
| R3 三方签字 | 4(标签 + 2 空段 + 日期) | 62.4 pt = 22.01 mm |
| R4 备注 | 2 | 31.2 pt = 11.01 mm |
| 合计 | 280.8 pt = 99.06 mm |
为什么左右页边距不对称
原表宽 150.32 mm,比 docx 版心(146.68 mm)宽 3.71 mm,而 tblPr 里没有
jc、tblInd 又是 0 —— Word 按左对齐处理:表从左边距 31.70 mm 起排,
右端溢进右侧页边距 3.71 mm。
所以本模板照实复现这个不对称:left = 31.70mm、totalwidth = 150.32mm,
右边界自然落到 210 − 31.70 − 150.32 = 27.98 mm。不要改成左右等边距
(那样表会整体左移 1.86 mm,与 Word 里看到的位置不符)。
四个必须知道的坑
1. ctex 的 \linespread ≈ 1.3 会吃掉你设的行距
\fontsize{14}{15.6}\selectfont % \baselineskip = 20.28pt ✗
\linespread{1}\relax\fontsize{14}{15.6}\selectfont % = 15.60pt ✓
\fontsize 第二个参数本该直接设定 \baselineskip,但 \selectfont 时会
再乘一次 \linespread。ctex 默认 \linespread ≈ 1.3,于是 15.6 pt 被放大
成 20.28 pt,五行表从 99 mm 涨到 129 mm,ht= 也压不住(内容本身就那么高,
tabularray 只能被动撑开)。
本包的对策是 \lwbodyfont:
\newcommand{\lwbodyfont}{\linespread{1}\relax\fontsize{14}{15.6}\selectfont}
\AtBeginDocument{\lwbodyfont}
\AtBeginDocument 那一次不能省——ctex 在 begin document 时会重设 \linespread。
2. 合并格 {c} 是不折行的 LR 模式
cell{r}{c}={c=n}{c} 的 {c} 是 LR 模式,不是段落模式:
- 格内文字比所跨各列合计更宽时,它不折行,而是把列撑宽、整表超出版心;
- 合并格里的
\hsize没有被重设成该跨度宽度,仍是外层版心宽, 长文本会按版心折行、冲出格子。
⇒ 合并格里的文字一律包定宽 \parbox,本表提供 \lwcell / \lwcellv /
\lwstack / \lwsign 四个入口,.tex 里只要有内容就用它们,别裸写。
3. 合并格的“内容可用宽”要补回 2·colsep·(n−1)
单列的内容宽 = wd(左右两半 colsep 已被 colspec 扣掉);
但跨 n 列合并后,n 列之间的 (n−1) 组内边距会被回收进内容宽:
内容可用宽 = Σ 所跨各列 wd + 2 · colsep · (n − 1)
漏算的后果很隐蔽:\parbox 比真实格宽窄,短文本看不出问题,长文本会
提前折行。本表「批准单位负责人签字」共 9 字 × 14 pt = 44.45 mm,
而 c1+c2 只有 41.37 mm,就曾被折成「批准单位负责人签」+「字」两行,
docx 里明明是一行。正确值是 41.3653 + 2×1.905 = 45.1753mm。
4. \parbox[t] 会让行高超目标
tabularray 给每个单元格垫了 strut,[t] 生成的盒子顶部与 strut 顶部相加,
实测每行白白多出 3.6 pt(R0 从 31.67 → 35.26)。
原表 R0C1 / R2C1 之所以“贴顶”,不是靠 vAlign=top(它们其实没写 vAlign),
而是靠段落数把行高填满(2 段 = 31.2 pt、9 段 = 140.4 pt,正好等于行高)。
内容高度已等于行高时,[c] 居中的结果就等同于贴顶。
⇒ .tex 里用 \lwcellv{内容\lwblank{n}} 补足段落数,\parbox 保持 [c]。
与 docx 的已知差异
| 项 | docx | 本模板 | 说明 |
|---|---|---|---|
| 表宽 | 150.319 mm | 150.137 mm | 差 0.18 mm。tabularray 计宽按 n−1 根竖线、画宽按 n 根,系统性差 0.5 pt,为消除 Overfull 微调了列宽 |
| 行高 | 11.01 / 5.50 / 49.53 / 22.01 / 11.01 mm | 每行 +0.15 mm | 同上,竖线计入行高 |
| 字体 | 宋体 | OUC Serif(思源宋体子集) | 原用系统 SimSun。现改为仓库公共字体包 ../fonts/,换机器/换系统都能编,且允许分发 |
| 标题 | 宋体 14 pt 加粗 | 同 | 字号与正文相同,只靠加粗区分 |
校验方法
不用肉眼看 PDF,直接量矢量:
pdftocairo -svg 劳务发放说明表.pdf out.svg # 取线条精确坐标
pdftotext -bbox 劳务发放说明表.pdf out.xml # 取每个字块的坐标
SVG 里解析 <path d="M x y L x y" transform="matrix(...)"/>,按
「|Δy|<0.9 且 |Δx|>10 = 横线」「|Δx|<0.9 且 |Δy|>2 = 竖线」分类去重,
即可得到每条线的精确端点。本模板的实测结果:
表宽 150.137 mm (目标 150.319,差 −0.18)
表左 31.662 mm (目标 31.700,差 −0.04)
表右 181.799 mm (目标 182.019,差 −0.22)
R0 11.172 / R1 5.668 / R2 49.646 / R3 22.159 / R4 11.172 mm 全部达标
三个签字格的标签基线完全重合(rel-top 1.06 pt),日期行也完全重合
(rel-top 47.69 pt)——这一条是特意做的:合并格与普通列的垂直居中算法
不同,若不统一包 \parbox,第一格会比另两格低 1.55 pt。