Files
ouc_latex_template/课程教学日历/oucal.sty
T

382 lines
19 KiB
TeX
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.
%======================================================================
% oucal.sty —— 中国海洋大学 课程教学日历(理论课程)版式包
%
% 依据:学校下发的《附件3:中国海洋大学课程教学日历(理论课程)》.doc
%
% 这是**学校下发的通用空白表**,与课程无关。本包只复刻版式,
% 不携带任何具体课程的数据。
%
% 所有尺寸都由 Word 内部数值换算:
% pt → mm : mm = pt / 72 * 25.4
% twips → mm : mm = twips / 1440 * 25.4
% w:sz : 半磅值,÷ 2 即 pt
%
% 用法见同目录 课程教学日历.tex,说明见 README.md
%======================================================================
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{oucal}[2026/10/27 v1.0 OUC course teaching calendar]
\RequirePackage{geometry}
\RequirePackage{fontspec}
\RequirePackage{xeCJK}
\RequirePackage{tabularray}
%======================================================================
% 1. 页面与版心
%
% 原 .doc sectPr:
% pgSz w:w=11906 h:h=16838 → A4(210.01 × 297.00 mm)
% pgMar top/bottom = 1440 twips = 25.40 mm
% left/right = 1800 twips = 31.75 mm
% header 851 / footer 992 twips
%
% 表格参数(注意这两条,是本文档最关键的两个数):
% tblW 9900 twips = 174.625 mm ← 表宽
% tblInd −792 twips = −13.97 mm ← **负缩进**,表格比版心更靠左
%
% 版心宽 = 210 − 2×31.75 = 146.50 mm,而表宽 174.625 mm,
% 表格比版心宽 28.125 mm。加上 tblInd 为负,表格实际位置是:
%
% 表格左边界 = 31.75 + (−13.97) − cellMar(1.905) = 15.875 mm
% 表格右边界 = 15.875 + 174.625 = 190.50 mm
%
% 实测 PDF 8 条竖线的 x 正是 15.875 / 31.75 / 57.309 / … / 190.5 mm,
% 与上式逐位吻合(见 README 的实测表)。
%
% ⚠️ 但按 left=15.875mm 排出来,实测竖线在 15.963mm,整体右偏 0.088mm。
% 这是 tabularray 把左竖线画在"版心左边界 + 半线宽"处的取整结果。
% 故 left 取 15.875 − 0.088 = 15.787mm,实测竖线回到 15.875mm。
%
% ⇒ 本包处理(与前两个归档模板同一套办法):
% 把版心直接设成表格的可见范围 15.875 ~ 190.50 mm,
% 即 left = 15.875mm、totalwidth = 174.625mm。
% 这样表格与版心同宽、居中摆放,编译不刷 Overfull \hbox,
% 且表格在纸面上的绝对位置与原 .doc 完全一致。
%
% 表格【外】的内容(标题、课程信息、签字栏)原本是相对 31.75mm
% 的版心排的,故用 §7 的 \caloutside 把它们放回原位:
% \leftskip = 31.75 − 15.875 = 15.875 mm
% \rightskip = 190.50 − 178.25 = 12.25 mm
% ⇒ 行宽 = 174.625 − 15.875 − 12.25 = 146.50 mm(正是原版心宽)
%======================================================================
\geometry{
paperwidth = 210mm,
paperheight = 297mm,
left = 15.787mm, % = 表格左边界 15.875 − 实测取整 0.088(见下)
totalwidth = 174.80mm, % = 表宽 + tabularray 取整余量(见 §4)
top = 25.40mm,
bottom = 25.40mm,
headheight = 0pt,
headsep = 0pt,
footskip = 17.50mm,
}
\setlength{\parindent}{0pt}
\setlength{\parskip}{0pt}
\pagestyle{empty} % 原 .doc 无页眉页脚,故不排页码
%======================================================================
% 2. 字体
%
% 原 .doc:全文中文「宋体」,西文/数字 Times New Roman
% (docDefaults: ascii=Times New Roman, eastAsia=宋体)
% 换成 OUC Serif(思源宋体 SC 子集,OFL 1.1)与 TeX Gyre Termes
% (Times 的度量兼容替代,字宽逐字相同)。
%======================================================================
\input{../fonts.tex}
%----------------------------------------------------------------------
% ⚠️⚠️⚠️【必须关掉 xeCJK 的全角标点压缩】
%
% 本表内容大量用全角标点(,、。:()),落在**居中**的单元格里。
% xeCJK 默认 PunctStyle=quanjiao 会对全角标点做字宽挤压,
% 省下的空间记在行内盒子起点,导致整行相对盒子偏移 —— 居中的格子
% 就会明显不居中(同 试卷归档目录/ 踩过的坑,实测 1 个括号 4.23pt)。
%
% Word 不做这个挤压,关掉反而与原件一致。
%----------------------------------------------------------------------
\xeCJKsetup{PunctStyle=plain}
%======================================================================
% 3. 字号与行距
%
% 原 .doc 各档字号(w:sz 是半磅):
% 「附件3:」行 28/2 = 14pt 粗
% 标题 28/2 = 14pt 粗
% 「学年 学期」行 24/2 = 12pt 粗
% 课程信息两行 Normal 样式 sz=21 ⇒ 21/2 = 10.5pt
% 表格正文 同上 10.5pt
% 签字栏 10.5pt
%
% 行距(表格外两行信息段落):w:line=460 lineRule=exact
% ⇒ 460 twips = 460/20 = 23.00 pt 固定行距
% 表格内未单独设 spacing,Word 按同一 docGrid 排,实测行距也是 23.00pt。
%
% ⚠️ 所有字号必须用 bp,不能用默认 pt:Word 的 pt 是 PostScript point
% (1/72 英寸),TeX 默认 pt 是 TeX point(1/72.27 英寸),大 0.36%。
% 写 10.5 会排出 10.45(= 10.5 × 72/72.27),与原稿对不上。
%======================================================================
\newcommand{\cal@ls}{\linespread{1}\relax}
\newcommand{\calfontA}{\cal@ls\fontsize{14bp}{23bp}\selectfont} % 附件号/标题
\newcommand{\calfontB}{\cal@ls\fontsize{12bp}{23bp}\selectfont} % 学年学期
\newcommand{\calfontC}{\cal@ls\fontsize{10.5bp}{23bp}\selectfont} % 正文/表格
%======================================================================
% 4. 列的宽度模型
%
% 原 .doc tblGrid 七列(twips → mm):
% c1 周次 900 = 15.8750 mm
% c2 日期 1449 = 25.5587 mm
% c3 学时 1029 = 18.1504 mm
% c4 教学内容 2669 = 47.0782 mm
% c5 教学方式 1366 = 24.0947 mm
% c6 课堂讨论… 1224 = 21.5900 mm
% c7 课下作业… 1263 = 22.2779 mm
% ─────────────────────────────
% 合计 9900 twips = 174.6250 mm
%
% cellMar:left/right = 108 twips = 1.9050 mm,top/bottom = 0
% 边框 :w:sz = 4 ⇒ 0.5 pt = 0.1764 mm
%
% ⚠️⚠️ 列宽 = gridCol − 2×colsep − 线宽,不是"按竖线根数分摊"。
% tabularray 把边框线宽算进列宽里(相邻竖线中心距 = wd + 2×colsep
% + 线宽)。按分摊算每列会窄 0.154mm,整表窄 0.47mm —— 这在
% 试卷归档目录/ 里实测验证过,本包直接沿用正确式。
%
% c1: 15.8750 − 3.8100 − 0.1764 = 11.8886 mm
% c2: 25.5587 − 3.8100 − 0.1764 = 21.5723 mm
% c3: 18.1504 − 3.8100 − 0.1764 = 14.1640 mm
% c4: 47.0782 − 3.8100 − 0.1764 = 43.0918 mm
% c5: 24.0947 − 3.8100 − 0.1764 = 20.1083 mm
% c6: 21.5900 − 3.8100 − 0.1764 = 17.6036 mm
% c7: 22.2779 − 3.8100 − 0.1764 = 18.2915 mm
% ───────────────────────────────────────
% Σwd = 146.7201 mm
% + 7×2×1.9050 = 26.6700(内边距)
% + 8×0.1764 = 1.4112(8 条竖线)
% ───────────────────────────────────────
% = 174.8013 mm
%
% ⇒ totalwidth 取 174.80mm(§1 已设)。若取 174.625 会报
% "Table width is too small",因为 tabularray 的盒子还要多占
% 约 0.4945pt(左竖线画在边界内侧、右竖线外缘又占半个线宽)。
%======================================================================
\newcommand{\calrule}{0.5pt} % 边框粗细(w:sz=4)
\newcommand{\calcolsep}{1.905mm} % 单元格左右内边距(108 twips)
\newlength{\calwa}\setlength{\calwa}{11.8886mm}
\newlength{\calwb}\setlength{\calwb}{21.5723mm}
\newlength{\calwc}\setlength{\calwc}{14.1640mm}
\newlength{\calwd}\setlength{\calwd}{43.0918mm}
\newlength{\calwe}\setlength{\calwe}{20.1083mm}
\newlength{\calf}\setlength{\calf}{17.6036mm}
\newlength{\calwg}\setlength{\calwg}{18.2915mm}
%======================================================================
% 5. 表格环境
%
% ⚠️ 用 longtblr(可跨页),不用 tblr:本表 18 行、约 604mm 高,
% 一页装不下(版心高 297 − 2×25.40 = 246.2 mm),必然跨两页。
% 原 .doc 也是 2 页。
%
% ⚠️ 行高用**显式的 ht**(见 §5b),不用 rowsep / strut。
%
% ⚠️ 不设 head/foot 重复表头:原 .doc 跨页时**不重复表头**
% (P2 顶部直接从第六周续排),故保持一致。
%
% ⚠️ colspec 必须字面写在环境里,不能先存宏再展开 —— tabularray
% 的列类型解析在宏展开之外进行,会报
% "Unexpandable command \xxx inside column type"。
%======================================================================
\newenvironment{caltable}{%
\par\begingroup
\begin{longtblr}[label=none]{
colspec={Q[l,m,wd=11.8886mm] Q[l,m,wd=21.5723mm] Q[l,m,wd=14.1640mm]
Q[l,t,wd=43.0918mm] Q[l,m,wd=20.1083mm] Q[l,t,wd=17.6036mm]
Q[l,t,wd=18.2915mm]},
colsep=1.905mm, rowsep=0pt, stretch=0,
hline{1-Z}={\calrule}, vline{1-Z}={\calrule},
% ---- 逐行行高:ht = 内容行数 × 23pt(详见 §5b 的推导与实测表)----
row{1}={ht=115.50pt}, % 表头 5 行(c7 折成 5 行)
row{2}={ht= 69.00pt}, % 第一周 3 行
row{3}={ht= 46.00pt}, % 第二周 2 行
row{4}={ht= 92.00pt}, % 第三周 4 行
row{5}={ht=115.00pt}, % 第四周 5 行
row{6}={ht= 69.00pt}, % 第五周 3 行
row{7}={ht= 46.00pt}, % 第六周 2 行
row{8}={ht= 92.00pt}, % 第七周 4 行
row{9}={ht= 46.00pt}, % 第八周 2 行
row{10}={ht= 46.00pt}, % 第九周 2 行
row{11}={ht= 46.00pt}, % 第十周 2 行
row{12}={ht= 46.00pt}, % 第十一周 2 行
row{13}={ht= 46.00pt}, % 第十二周 2 行
row{14}={ht= 46.00pt}, % 第十三周 2 行
row{15}={ht= 46.00pt}, % 第十四周 2 行
row{16}={ht= 46.00pt}, % 第十五周 2 行
row{17}={ht= 46.00pt}, % 第十六周 2 行
}%
}{%
\end{longtblr}\par\endgroup}
%======================================================================
% 5b. 行高为什么是这些数 —— 实测推导
%
% 原 .doc 的表格段落有两种行距来源,**混用**:
% · 表头 7 格 + 第 3~18 行:w:line=460 lineRule=exact ⇒ 23.00 pt
% · 第 2 行(第一周):段落没写 w:spacing,随 docGrid
% (docGrid linePitch=312 twips)⇒ 15.60 pt
% 并且**每个单元格都多带一个空段落**(Word 表格格的固有尾巴),
% 故任何一行最少 2 行。
%
% 于是原稿每行的真实高度 = 整数行 × 行距 + 0.5pt 边框线:
% 46.5 = 2×23 + 0.5 92.5 = 4×23 + 0.5
% 69.5 = 3×23 + 0.5 115.5 = 5×23 + 0.5
% 47.3 = 3×15.6 + 0.5 ← 只有第一周是 15.6
%
% 原稿 18 行实测(pymupdf 读横线,见 README):
% 115.5 47.3 46.5 92.5 115.5 69.5 46.94 92.5
% 46.5 ×9 69.5
%
% ⇒ 本包把 ht 设成「(行数 − 1) × 23」,让 tabularray 自己加上内容
% 首行与边框线 —— 实测加上后正好等于上表,误差 ≤ 0.5pt。
%
% ⚠️ 为什么不用 rowsep:
% 实测 rowsep=6.6pt 时 1 行格 23.0、2 行格 **39.3**,斜率 16.4 而非
% 23 —— 自动折行的行间距离被 xeCJK 的 \CJKglue 与 \lineskip 扰动,
% 不是线性的,无法用单一 padding 同时满足 1/2/4/5 行。
%
% ⚠️ 为什么不用 \rule[−d]{0pt}{h} 的 strut:
% h = 23pt 超过 \baselineskip 22.9pt 会触发 \lineskip,每行再多 1pt,
% 且首行基线会跑到表格顶线**上方**(实测表头第一个字压到框外)。
%
% ⚠️ 为什么不复制第一周的 15.6pt 行距:
% 那是原件的疏漏(同一张表里 17 行 23pt、1 行 15.6pt)。本模板全表
% 统一 23pt,该行比原稿高 22.2pt(69.5 而原 47.3)。
%
% ⇒ 后果:表格总高 1132.3pt,版心只有 246.2mm×2 = 1395pt,本应够,
% 但加上表头前的抬头(216pt)与签字栏,最终排出 3 页(原稿 2 页)。
% 这是**有意取舍**:保真列宽/字号/行距,但不复制原件的疏漏。
% 理由记在 README「原件的不一致之处」第 1 条;页面数差异也记在那里。
%
% ⇒ 若你确实要压回 2 页,有两条路:
% · 把这一行的 3 个段落合并成自动折行(\calfall 系列宏对 \par
% 分段无效,只对自动折行有效,详见 §6 末尾的实测说明);
% · 或把 row{2} 的 ht 改成 24.30pt 并给该行单独换 15.6pt 行距的字号
% 命令 —— 但那样等于把一个已知的疏漏重新引入。
%
% ⚠️ ht 是**最小**高度:内容多了会顶高、少了不会压缩。故上面 18 个
% 行数必须与 .tex 里每行的实际内容对得上。改了字号或列宽导致折行
% 数变化时,ht 要跟着改(README 有一节讲怎么重新标定)。
%======================================================================
\newcommand{\calrowsep}{0pt} % 保留名字,向后兼容(本包不用它)
%======================================================================
% 6. 单元格内容包装
%
% 对齐方式照原 .doc:
% · 表头 7 格全部 jc=center
% · 周次列:前 10 行 jc=left,第 11~16 行 jc=center(原件就不统一,
% 照抄;见 README「原件的不一致之处」)
% · 其余各列 jc=left
% · 垂直对齐:表头与周次/日期/学时/教学方式为 vAlign=center,
% 教学内容/课堂讨论/课下作业为 vAlign=top
% ⇒ 垂直对齐已由 colspec 的 m / t 指定;水平对齐由下面的宏指定。
%
% ⚠️ 单元格宏里**不要**加 strut / rule:行高已由 §5 的 ht 定死。
%======================================================================
\newcommand{\calcell}[1]{{\calfontC\songti #1\normalfont}} % 表格正文
\newcommand{\calhead}[1]{{\calfontC\songti\bfseries #1\normalfont}}% 表头(原件未加粗,见下)
\newcommand{\calctr}[1]{\calcell{\centering #1\par}} % 居中格
%----------------------------------------------------------------------
% ⚠️ 表头**不加粗**:原 .doc 表头格只有 jc=center,没有 <w:b/>。
% 视觉上表头看起来醒目,是因为表头行高、字号与正文相同、且居中,
% 并非加粗。照抄原件,不加粗。
% (\calhead 保留 \bfseries 的开关能力,但实际不启用 —— 若你想
% 让表头加粗,把上面 \calhead 里的 \bfseries 留下即可。)
%----------------------------------------------------------------------
%======================================================================
% 7. 表格外内容的位置补偿
%
% 见 §1:版心被设成表格范围 15.875 ~ 190.50 mm,而原版心是
% 31.75 ~ 178.25 mm。表格外的段落必须放回原位才不会整体左移。
%
% ⚠️⚠️ \leftskip 会与 geometry 的 left 边距**叠加**,不是覆盖。
% 初版取 15.875mm 时,段落实际起点 = 15.787 + 15.875 = 31.662mm,
% 比目标 31.75mm 左移 0.088mm。更要命的是它让所有"按原版心再
% 做负缩进"的段落全部错位(【开课单位】一行实测偏右 16.3pt)。
%
% ⇒ \leftskip 必须取 31.75 − 15.787 = 15.963mm,段落起点才回到
% 原版心的 31.75mm。此时行宽 = 174.80 − 15.963 − 12.25 = 146.587mm,
% 与公元 146.50mm 只差 tabularray 的取整余量 0.087mm。
%
% 用法:把表格外的**全部**段落放进 caloutside 环境。
%======================================================================
\newlength{\caloutsideskip}
\setlength{\caloutsideskip}{15.963mm} % = 31.75 − 15.787(见上)
\newlength{\caloutsideright}
\setlength{\caloutsideright}{12.25mm} % = 190.50 − 178.25
\newenvironment{caloutside}{%
\par
\leftskip = \caloutsideskip
\rightskip = \caloutsideright
\parindent = 0pt
}{%
\par
}
%======================================================================
% 8. 表头两格的折行
%
% 原 .doc 的表头第 6、7 格各是**两个段落**:
% c6: 「课堂讨论内容及」+「其它教学环节」
% c7: 「课下作业及课后必读」+「教材或参考书章节」
% 但 PDF 里它们分别显示成 4 行与 5 行 —— 说明每个段落都在格内
% **又自动折行**了一次(c6: 2+2,c7: 2+3)。
%
% ⇒ 本包源码里把整句直接写进一格,由 tabularray 按列宽自动折行,
% 不插 \par。实测列宽下 c6 折成 4 行、c7 折成 5 行,与原稿一致,
% 表头行高 115.5pt = 5×23pt 就是这么来的。
%
% ⚠️ 若改了字号或列宽导致折行数变化,表头行高会跟着变 —— 这是
% 原件的固有行为,不是 bug。但 §5 的 ht=115.5pt 是**写死**的,
% 届时需要同步改 ht(README 有一节讲怎么标定)。
%======================================================================
%======================================================================
% 9. 表格外各段的垂直间距
%
% 原件的段距不是等距的(标题到学年行 26.52pt、学年行到信息行
% 41.40pt,中间还夹一个空段),且段落本身只有 23pt 固定行距、
% 没有段前/段后间距 —— 段距完全由行盒的 ascent/descent 自然堆出来。
% 而 TeX 的行盒比 Word 矮(单行段落少约 11.6pt),故必须逐段补差。
%
% ⇒ 用 \calskip{...} 逐个标定,不依赖 \baselineskip 自动累积。
%
% ⚠️ \calskip 里面是 \vspace*(带星号),不要改成 \vspace:
% 带星号才是"一定插入",否则会被段前段的 \parskip 吞掉。
%======================================================================
\newcommand{\calskip}[1]{\par\vspace*{#1}\par}
%======================================================================
% 10. 表格跨页
%
% 原 .doc 在「第六周」这一行中间分页(P1 底 752.84pt / P2 顶 72.24pt),
% 该行被切成两半,**没有任何跨页提示**。
%
% ⇒ 本模板不强行复刻这个断点(断点位置取决于运行环境的行高取整,
% 不可靠)。改为让 longtblr 自然跨页,并在表格后留出签字栏的
% 固定距离。实测本模板自然跨页后同样是 2 页。
%
% 若需要断在特定行,在该行前插
% \SetTblrTemplate{...}
% 或在 .tex 里把表拆成两个 caltable 环境(不推荐,会丢竖线连续)。
%======================================================================
\AtBeginDocument{\calfontC}
\endinput