Files
ouc_latex_template/成绩构成说明/README.md
T
laoyang 95afc9caa6 成绩构成说明:表格格线全画 + 签字栏去掉年份占位 #9
用户反馈:中间表格要把线都画上,中间空白容易看错行;最后的年月日
也不要带 20xx。

① 表格格线全画
   hline{1,Z}+hline{2}+vline{1,Z} → hline{1-Z}+vline{1-Z}
   只画外框时数据行之间、列之间全空,跨行格与单行格的分界看不出。

   连带必须改列宽:画上 5 条内部竖线后表宽多 2.5pt,报
   Overfull 2.47554pt,末列 30.28mm → 29.40mm 补回。

   连带暴露出被掩盖的缺陷:「理论课」左跨 2 行,右格「平时成绩
   (作业20%、点名10%)」只占 row2 一行、在 26mm 列里折 3 行,
   自然高 54.73pt —— row2 被撑到 54.73pt,文字上下零留白,末行
   「10%)」墨迹底离行线仅 0.90pt(0.32mm),其他行是 4.97pt。
   画满格线前那里本来没有线,缺陷完全看不到。

   治法 row{2}={ht=22mm}。关键是 ht 为最小高度、须大于内容高才生效:
   先试 12.5mm(35.4pt < 54.73pt)无反应,22mm(62.4pt)才把行撑到
   62.86pt,末行墨迹间隙 4.08pt(1.44mm),与其他行同量级。

② 签字栏去掉年份
   20××~年 月 日 → 年 月 日。签名文件的年份由签字人当场手写,
   模板预留占位符反而占掉手写空间。

③ 修掉 3 处真实数据泄漏(本单上一版遗留)
   README 引用原件全文处、README「真实数据零泄漏」一节(这一节
   为了证明零泄漏反而把真实课程名/学年/教师名原样列出)、
   .tex 的「闭卷笔试」。已复查 10 个关键词零命中。

验收:1 页、零 Overfull/Underfull/Missing character;格线逐条清点
(跨行格内无中线、合计行跨 6 列无内部竖线);逐字符交叉校验越列 0
处、越行 0 处;内容底 764.71pt vs 版心底 769.89pt,余量 5.18pt。
2026-10-27 09:45:58 +08:00

359 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 成绩构成说明 — 中国海洋大学《成绩构成说明》(LaTeX 版)
把任课教师手写的「成绩构成说明」做成 LaTeX 模板。原件是一份填过的
`.doc` 实例(两段话 + 标题,无表格、无签字栏),本模板在**保留其信息
内容**的前提下重新组织,并补齐归档必需项。
> **本模板不含原件任何真实数据。** 课程名、学年、学期、教师姓名、
> 所有比例、日期一律 `××××` / `20××` / `×` 占位,已 grep 验证零命中。
## 目录
- [文件](#文件)
- [编译](#编译)
- [改内容](#改内容)
- [相对于原件的改动](#相对于原件的改动)
- [比例自洽校验](#比例自洽校验)
- [版式参数](#版式参数)
- [五个坑](#五个坑)
- [真实数据零泄漏](#真实数据零泄漏)
- [字体](#字体)
## 文件
| 文件 | 说明 |
| :-- | :-- |
| `成绩构成说明.tex` | 主文件。填表只改第 1 节(10 个宏) |
| `oucgrade.sty` | 版式包。页面/字体/表格/签字栏,附换算注释 |
| `成绩构成说明.pdf` | 成品样张(1 页) |
## 编译
```bash
xelatex 成绩构成说明.tex
```
**单页,跑一遍即可**(无交叉引用,不需要 bibtex)。
⚠️ 必须在**本目录**下编译,不要从仓库根调用 —— `fonts.tex` 用的是
相对路径 `../fonts/`。
需要 XeLaTeX,并安装 `ctex`、`geometry`、`fontspec`、`xeCJK`、
`tabularray`、`enumitem`、`graphicx`。实测环境:TeX Live 2022。
## 改内容
### 课程信息(10 个宏)
```latex
\newcommand{\grdcourse}{××××} % 课程名称
\newcommand{\grdyear}{20××} % 学年
\newcommand{\grdseason}{×} % 季学期(春 / 秋)
\newcommand{\grdteacher}{×××} % 任课教师
\newcommand{\grdtheory}{80} % 理论课占总成绩 %
\newcommand{\grdlab}{20} % 实验课占总成绩 %
\newcommand{\grddaily}{30} % 平时成绩占理论课 %
\newcommand{\grdfinal}{70} % 期末考试成绩占理论课 %
\newcommand{\grdhw}{20} % 作业占理论课 %
\newcommand{\grdattend}{10} % 点名占理论课 %
\newcommand{\grdlabnote}{由实验课教师根据实验报告与实验操作评定。}
```
**只填数字,不要带 `%` 号**,百分号由模板加。
### 「占本类」与「占总成绩」是两回事
这是本模板最要紧的一个设计,见下节。
### 改写正文
- 总述:`\grdpara{...}`
- 分节:`\grdsec{一、成绩构成}`
- 列表:`\begin{grdlist} \grditem{项}{:说明} \end{grdlist}`
- 表格单元格:`\grdth{}`(表头)/ `\grdtd{}`(表身)
## 相对于原件的改动
原件全文只有这些内容:
```
成绩构成说明
×××× 20××学年 ×季学期 期末考试
理论课成绩占总成绩的80%。理论课成绩包括平时成绩、期末考试成绩。
平时成绩占30%(作业占20%,点名占10%),期末考试成绩占70%。
实验成绩占总成绩的20%。由实验课老师提供。
```
它的问题有四个,逐个解决:
### 1. 三层百分数混用不同基准,读者要心算三轮
| 句子 | 百分数 | **基准实际是** | 折总成绩 |
| :-- | --: | :-- | --: |
| 理论课占总成绩的 80% | 80% | 总成绩 | 80% |
| 平时成绩占 30% | 30% | **理论课** | 24% |
| 作业占 20% | 20% | **理论课** | 16% |
| 期末考试成绩占 70% | 70% | **理论课** | 56% |
「平时占 30%」看着像占总成绩三成,实际上只占 **24%**。教务抽查时
这里最常被挑错,学生也容易误读。
**改法**:表格左右并置「占本类」与「占总成绩」两列,后者由
`\grdcalc{理论课\%}{分项\%}` 自动算出,不给心算留余地。
### 2. 层级关系埋在句子里
「平时成绩占30%(作业占20%,点名占10%)」—— 括号里的两个数加起来
才是括号外的数,这种嵌套在一个句子里读起来要来回扫。
**改法**:改成 6 列表格,一级项目跨行合并,层级一眼可见。
### 3. 没有说明「怎么算」
原件说了比例,没说作业怎么平均、缺交怎么扣、点名缺几次记零。
这些是学生真正会问的,也是申诉时的依据。
**改法**:新增「二、分项说明」一节,用列表逐项写清(文字留占位,
任课教师按自己课程的规则填)。
### 4. 没有签字栏
**这是用户明确要求补的。** 没有任课教师签字的成绩构成说明,在教务
归档时不被认作有效文件。
**改法**:右下角落款,含签字线与「年 月 日」三个手写填空位。
⚠️ **「年 月 日」不印年份**。成绩构成说明是学期末签署的文件,年份
由签字人当场手写;模板里预留 `20××` 反而会占掉手写空间、并诱导
使用者去改占位符。所以这一行只有三个字 + 两处 `\hspace` 间距。
### 另外新增(可关闭)
「三、成绩评定与异议」一节 —— 说明总成绩如何取整、异议如何在几个
工作日内提出。原件没有,但归档材料有这一节更完整。
不要的话把 `.tex` 里这个开关改成 0:
```latex
\def\grdwithini{0} % 1=印这一节,0=不印
```
⚠️ 关掉后正文变短,签字栏会往上移。若想让它仍贴底部,把 `oucgrade.sty`
里签字块前的 `\par\vspace{3mm}` 调大即可(见[坑 3](#坑-3签字栏被挤到第-2-页))。
## 比例自洽校验
模板会检查三组加法:
```
平时 + 期末 = 100 (构成全部理论课)
作业 + 点名 = 平时 (构成全部平时成绩)
理论课 + 实验课 = 100 (构成全部总成绩)
```
不通过时:
1. `.log` 里打出
`*** oucgrade: 比例不自洽 --- 作业+点名 应构成全部平时成绩: 实际 25+10, 应为 30 ***`
2. PDF **标题正下方**印一行红字警告
**为什么值得做**:成绩构成说明是要归档、要被抽查的文件,比例加不起来
是最常见也最难被自己发现的错误 —— 原件那句「平时 30%(作业 20%,
点名 10%)」正是靠心算才看得出 20+10=30。
**已做负面测试**:把 `\grdhw` 从 20 改成 25 重编,log 报
`实际 25+10, 应为 30`,PDF 第 1 页顶部出现警告;改回 20 后警告消失。
⚠️ 警告印在**开头**不是文末。文末的提示会被签字栏挤到第 2 页,
而第 2 页通常没人翻 —— 等于没提示。
## 版式参数
### 页面
| 项 | 值 | 来源 |
| :-- | --: | :-- |
| 纸张 | A4 210×297mm | `pgSz 11906×16838` twips |
| 上下边距 | 25.40mm | `pgMar top/bottom 1440` |
| 左右边距 | 31.75mm | `pgMar left/right 1800` |
| 版心 | 146.50 × 246.20mm | 210−2×31.75 / 297−2×25.40 |
| 页脚距 | 17.51mm | `pgMar footer 992` |
换算:`mm = twips / 1440 × 25.4`,`pt = twips / 20`。
### 字号
| 用途 | 原件 (w:sz) | 本模板 | 实测输出 |
| :-- | --: | --: | --: |
| 大标题 | 36 半磅 = 18pt | `18bp` | 18.00 |
| 副标题 | 24 = 12pt | `12bp` | 12.00 |
| 正文 | 28 = 14pt | `14bp` | 14.00 |
| 表格/签字 | — | `12bp` | 12.00 |
⚠️ **必须写 `bp` 不能写 `pt`**。Word 的 pt 是 PostScript pt(1/72 in),
TeX 默认 pt 是 1/72.27 in,差 0.36%。写 `14pt` 实测输出 14.05。
### 行距
| 用途 | 值 | 说明 |
| :-- | --: | :-- |
| 正文 | 21.5bp | 14pt 的 1.54 倍 |
| 表格/签字 | 17bp | 12pt 的 1.42 倍 |
原件 `docGrid linePitch=312` = 15.6pt,对 14pt 字只有 1.11 倍,太挤。
本模板用 21.5bp(1.54 倍)—— 这是**有意的改善**,不是复刻。
21.5 是为压到一页反复试出来的值,可读下限约 1.4 倍,还有余量。
### 表格列宽
6 列,Σ wd = 123.64mm:
| 列 | 宽 | 内容 |
| :-- | --: | :-- |
| 项目 | 22.00mm | 理论课 / 实验课 |
| 占本类 | 14.00mm | 80% |
| 细分项目 | 26.00mm | 平时成绩 / 期末 |
| 占本类 | 14.00mm | 30% |
| 占总成绩 | 17.00mm | 24% |
| 评定依据 | 29.40mm | 作业 + 出勤 |
**Σwd 为什么是 123.64 而不是 146.50**:`Q[wd=...]` 是内容区宽度,
不含 `colsep`。每列实际占位 = `wd + 2×colsep`,6 列共多占
`6×2×1.905 = 22.86mm`。所以
`Σwd = 146.50 − 22.86 = 123.64mm`。
⚠️ **画满全格线之后每列还要再扣 0.5pt**。只画外框时表宽里不含内部
竖线;一旦 `vline{1-Z}` 把 5 条内部竖线都画上,表宽就多出
`5×0.5 = 2.5pt`,编译报 `Overfull \hbox (2.47554pt too wide)`。
所以最后一列从 30.28mm 收到 29.40mm(−2.5pt)补回来。
### 行高:`ht` 是最小值,且跨行格不会自己长高
这两点一起踩过,都表现为"字贴着格线":
| `ht` 是否小于内容高 | 结果 |
| :-- | :-- |
| 是(如 `ht=7.5mm` < 54.73pt) | 行被内容撑到 54.73pt,**上下零留白**,末行墨迹底离行线只剩 0.90pt(0.32mm),其他行是 4.97pt(1.75mm)—— 一眼看出挤 |
| 否(`ht=22mm` = 62.4pt) | 行高 62.86pt,上下各留约 3.8pt,末行墨迹底离行线 4.08pt(1.44mm),与其他行同量级 |
所以 **`ht` 给了不等于生效,必须大于内容自然高度**。本例「平时成绩
(作业 20%、点名 10%)」在 26mm 列里折 3 行,自然高 54.73pt
(= 2×17bp + 盒高),要撑开就得给到 22mm。
`ht` 按**表格行号**给,不是按逻辑块:`row{1}` 是表头、`row{2}` 是
「理论课/平时成绩」那一行。抬高 `row{3}`(「期末」行)只会移动中间
那条线,对跨行块的高度没有影响。
## 五个坑
### 坑 1:`.tex` 里写带 `@` 的宏名
`.tex` 文档中 `@` 的 catcode 是 12(其他),不是 11(字母)。写
`\grd@chk` 会被解析成 `\grd` + `@chk`,报
`! LaTeX Error: Missing \begin{document}`,并且**连累后面所有内容**
(报一串 Undefined control sequence)。
**规矩**:带 `@` 的内部宏一律定义在 `.sty` 里,`.tex` 只调用不带 `@`
的公开接口(如 `\grdcheck`)。
### 坑 2:tabularray 的 `hline{}` 里不能用带 `@` 的宏
```latex
hline{1,Z}={\grd@rulev} % ✗ 报 \l__tblr_v_tl ->\grd @rulev
hline{1,Z}={0.5pt} % ✓ 写字面值
```
tabularray 把 `hline` 的参数当**字符串重新 tokenize**,那一刻 `@` 的
catcode 已经退化,宏名在 `@` 处断开。
**规矩**:`hline` / `vline` 的线宽写字面值,别用宏包一层。
⚠️ 关于 `1-Z` 与 `1,Z`:
| 写法 | 含义 | 效果 |
| :-- | :-- | :-- |
| `hline{1,Z}` | 第 1 条 + 最后 1 条 | 只有顶框和底框 |
| `hline{1-Z}` | 第 1 条到末条(全部) | 每条行线都画 |
本模板用 `1-Z`:**所有格线都画满**。只画外框时,跨行格(理论课)
与单行格的分界、以及数据行之间全是空白,非常容易看错行。这是
用户明确要求改掉的点。
代价是每列要再扣 0.5pt 表宽(见上文「画满全格线之后每列还要再扣」
一节),并且暴露出一个原本被掩盖的缺陷:跨行格里的第 3 行字一直
是贴着格线的(详见上文「行高」一节)。**画满格线会让所有"零留白"
的排版问题显形** —— 这是好事,但要预留调 `ht` 的时间。
### 坑 3:签字栏被挤到第 2 页
试过四种写法,只有最后一种同时满足「一页」+「不出界」:
| 写法 | 结果 |
| :-- | :-- |
| `\par\vspace{4mm}` + minipage | ✗ 翻页(minipage 紧跟 `\par` 是合法断页点) |
| `\vfill` 顶到底部 | ✗ 不翻页但**出界**:年月日 y=774.9,版心底只有 771.6 |
| `\vfill` + 块内 `\vspace*{5mm}` | ✗ 又翻页(`\vfill` 与块高之和超出剩余空间时,TeX 不接受压缩 `\vfill`,只能整块搬迁) |
| `\par\vspace{3mm}` + minipage | ✓ 一页,内容底 769.8,余 1.8pt |
**结论**:不要指望竖直胶帮你「自动贴底」。单页文档里最稳的是
**固定间距 + 实测校准** —— 改正文后重编一次,看内容底距 771.6 还有多少。
### 坑 4:minipage 内部用 `\hfill` 做右对齐
```latex
\begin{minipage}{76mm}
任课教师(签字):\rule{40mm}{0.4pt}\par % 行尾没有 \parfillskip 弹性
\end{minipage}
```
minipage 里的行末会被拉伸到满宽,12pt 的字出现夸张字距 ——
实测「任」「课」「教」「师」被拉到 **34.8pt 一字**。
**正解**:minipage 内 `\raggedright`,`\hfill` 只放在外面:
```latex
\noindent\makebox[\textwidth][r]{\begin{minipage}{76mm}\raggedright ...\end{minipage}}
```
### 坑 5:列表引导符「·」缺字
本库 OUC Serif 子集里**没有** `·`(U+00B7) 和 `•`(U+2022),
用它们会报 `Missing character: There is no · (U+00B7)`,印出来是空白。
实测该字体有 `●`(U+25CF) 与 `○`(U+25CB),且 `fonts.tex` 已把这两个
码位归入 CJK 字符类,可直接用。本模板用缩小 0.6 倍的 `●` 作引导符。
## 真实数据零泄漏
原件是**填过的实例**(真实课程名、学年、教师姓名、全部比例)。
本模板把这些逐项换成占位符。
已 grep 确认以下关键词在 `oucgrade.sty`、`成绩构成说明.tex` 与 `README.md`
中**零命中**(含注释):原件实例的课程名、学生人数、开课号、教师姓名、
单位名称,以及原件出现过的具体年份。本 README 不列出这些原文,
避免「为了证明零泄漏而把真实信息写进来」。
注意:`80`/`20`/`30` 这些纯数字作为**默认占位值**是存在的(宏定义里),
但它们不代表原件真实比例,替换时改掉即可。真正需要保证零命中的是
课程名、年份、教师名这类可识别的信息。
## 字体
全部来自仓库公共字体包 `../fonts.tex`(唯一允许声明字体的地方):
| 用途 | 字体 | 授权 |
| :-- | :-- | :-- |
| 正文宋体 | OUC Serif(思源宋体 GB2312 子集) | SIL OFL 1.1 |
| 标题楷体 | OUC Kai(霞鹜文楷子集) | SIL OFL 1.1 |
| 分节黑体 | OUC Sans(思源黑体子集) | SIL OFL 1.1 |
| 西文 | TeX Gyre Termes | GUST Font License |
⚠️ 原件标题用的是**方正楷体_GB2312**,那是商业字体,本库不含也不能分发。
改用霞鹜文楷(同属楷书形态,开源可商用)。这是**有意的替换**,不是遗漏。
⚠️ 不要用 `\setCJKmainfont` 直接指定系统字体(SimSun / SimHei 等)——
换到 Linux / macOS 就编不出来。全部走 `../fonts.tex`。