Files
ouc_latex_template/会议预算表/oucmeeting.sty
T
laoyang f42e6e398a 会议预算表:修正四处排版问题 #24
用户反馈四点,逐条修。

## ① 会议时间与会议地点位置错乱

**现象**:两个字挤在一起,中间没有留填写区。

**根因**:tabularray 的 cell{} 合并位置决定标签落在哪一格,
不是"第几个字"。R2 的合并是 2+2+2+1,即
   格1(列1-2)=会议时间   格3(列3-4)=空值
   格5(列5-6)=会议地点   格7(列7)=空值
「会议地点」必须写在第 5 段,我原先写在第 3 段。

**顺带查清 tabularray 的一个静默失败**(最小复现):
写 7 段 `甲 & & 乙 & & 丙 & & 丁` → 四个字全对
写 4 段 `甲 & 乙 & 丙 & 丁`         → **乙、丁 被吞掉,不报错**
⇒ 段数必须等于列数,被合并的格留空。

**修正**:`会议时间 & & & & 会议地点 & & \\`
实测:会议时间 x=118.6(列1-2),会议地点 x=324.1(列5-6),
中间 193~289 是填写区。

## ② 其他费用应为竖排,不是旋转 90°

**根因**:`\rotatebox{90}` 只是把横排整句转个角度,
不是"上下排列"。用户要的是逐字竖着叠。

**修正**:改 `\shortstack{其\\[17bp]他\\[17bp]费\\[17bp]用}`,
并用 `cell{21}{1}={r=5}{c}` 做纵向合并 5 行。

实测:格高 82bp(y 517.9~611.7),四字 y=517.7/546.4/574.6/602.9,
字距 28.7bp,基本填满。

⚠️ 拉字距**不能**用局部 `\baselineskip` —— `\shortstack` 内部会重置它,
实测设 19bp 完全无效(字距仍是 11.6bp)。必须在 `\\` 上给可选参数。
⚠️ 也**不能**用 `\parbox{}{其\\他\\费\\用}` —— 会变成 4 个独立行盒子,
tabularray 排版错乱,四个字跑到不同段落(实测)。

⚠️ 之前认为「纵向合并和横向合并不能共存」,那是因为写在内容里用
`\SetCell[r=n]`;改用 cell{} 矩阵语法后**可以共存**,本条即证据。
第 2 节的说法已相应更正。

## ③ 校级领导意见是多余的

**先核实**:源 .doc 的 R26 第二格里**确实有两个段落**——
「所在单位负责人意见:」和「校级领导意见:」。
用 LibreOffice 直接转 txt 核过,原文件就有,不是转换时多出来的。

**但用户确认这份表不需要校级领导,故删除。**

代码里注明了原文件有两个段落,要恢复时加回来且必须单独一行。

## 顺带

去掉 `\RequirePackage{graphicx}` —— 不再用 \rotatebox,减少一个依赖。

## 验证

   1 页   error 0   Overfull 0   Underfull 0
   表格 155.52mm / 版心 155.99mm  界内
   最右 521.58(版心止 521.57)  最下 722.27(版心底 742.65)
   校级领导意见 出现 0 次
   会议时间/会议地点 相距 205bp 中间留空

全库 15/15 通过。双端一致:
   WSL TeX Live 2025     1 页 err=0 over=0 under=0
   Windows TeX Live 2022  1 页 err=0 over=0 under=0
2026-10-30 20:11:50 +08:00

266 lines
13 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.
%======================================================================
% oucmeeting.sty —— 中国海洋大学 会议预算表 版式包
%
% 由学校下发的 .doc 复刻而来(源文件:会议预算表.doc,OLE2 老格式,
% 先经 LibreOffice 转 docx 再解析 OOXML)。尺寸全部从原文件量出。
%
% ⚠️ 编译: xelatex 会议预算表.tex
% 单次编译即可,无交叉引用。
% 依赖 ctex + geometry + fontspec + xeCJK + tabularray
% 与 课程大纲/、课程执行大纲/ 同源同风格。
%======================================================================
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{oucmeeting}[2026/10/30 v1.0 OUC meeting budget form]
\RequirePackage{geometry}
\RequirePackage{fontspec}
\RequirePackage{xeCJK}
\RequirePackage{tabularray}
% 注:不再需要 graphicx。早期版本用 \rotatebox 做「其他费用」竖排,
% 现已改为 \shortstack 逐字竖排(#24 按用户意见修正),不再依赖 graphicx。
%======================================================================
% 1. 页面
%
% 原 docx sectPr 实测(11906 x 16838 twips):
% 宽 210.01 mm 高 297.00 mm ← 标准 A4
% top 2155 twips = 38.01 mm
% bottom 1985 twips = 35.01 mm
% left 1588 twips = 28.01 mm
% right 1474 twips = 26.00 mm
% header 0 twips = 0.00 mm (无页眉)
% footer 992 twips = 17.50 mm
%
% 四个边距都不常见(38/35/28/26),是原表为把 28 行表格塞进一页
% 压出来的。照抄,不"优化"——这是复刻件,不是重新设计。
%
% ⚠️ 版心宽 = 210 - 28.01 - 26.00 = 155.99 mm
% 而原表宽 169.12 mm,**超宽 13.12 mm**。
% 原 docx 用 tblLayout=fixed 硬撑,实际会压进右侧页边距。
% ⇒ 本包把表格等比缩到 155.99 mm(系数 0.9224),
% 列宽按比例重算,见第 3 节。这是唯一一处主动偏离原文件的地方。
%======================================================================
\geometry{
paperwidth = 210mm,
paperheight = 297mm,
left = 28.01mm,
right = 26.00mm,
top = 38.01mm,
bottom = 35.01mm,
headheight = 0pt,
headsep = 0pt,
footskip = 17.50mm,
}
\setlength{\parindent}{0pt}
\setlength{\parskip}{0pt}
\pagestyle{empty} % 原 docx 无页眉页脚页码
%======================================================================
% 2. 字体
%
% 原 docx 用宋体 / 黑体。仓库规矩(#17):不用商业字体,
% 全部取自公共字体包 ../fonts.tex。
%
% ⚠️ 原表字号分布实测:
% 22pt ×1 —— 标题「中国海洋大学会议预算表」
% 9pt ×47 —— 表格内全部文字 + 三条说明
% 无 12pt 段落,与课程大纲那套不同,别照抄那边的字号命令。
%======================================================================
\input{../fonts.tex}
\newcommand{\oucMeetFont}[2]{\fontsize{#1pt}{#2pt}\selectfont}
\newcommand{\Mtw}{\oucMeetFont{22}{30}} % 标题 22pt
\newcommand{\Mn}{\oucMeetFont{9}{12}} % 表格与说明 9pt
\AtBeginDocument{\Mn}
%======================================================================
% 3. 表格列宽
%
% 原 docx 网格(tblGrid,单位 twips):
% [424, 2054, 790, 1262, 2056, 209, 2793] 合计 9588
% 换算 mm(×25.4/1440):
% [7.48, 36.23, 13.93, 22.26, 36.27, 3.69, 49.27] 合计 169.12
%
% 等比缩到版心 155.99 mm(系数 0.9224):
% [6.9, 33.4, 12.9, 20.5, 33.4, 3.4, 45.4] 合计 155.9
%
% ⚠️ 第 6 列只有 3.4mm,是原文件里「其他费用」竖排标题那一列的残留
% (原 209 twips,几乎只是一条分隔线宽)。保留它是因为第 20~24 行
% 的「其他费用」合并单元格需要它占位,删掉会破坏网格。
%======================================================================
% ⚠️ 列定义**不能**放进宏再用 \OucMeetColspec 引用 —— tabularray 的
% colspec 解析器要求字面量,报
% Package tabularray Error: Unexpandable command ... inside Column type
% ⇒ 直接写在下方的 colspec={...} 里(实测唯一可行写法)。
%----------------------------------------------------------------------
% 行高(#24 修正)
%
% ⚠️⚠️ 原 docx 的 28 行里**只有 R26 显式设了 trHeight**(567 exact = 10mm),
% 其余 27 行都没设 —— 行高由内容自然撑开,9pt 字约 4.5~5mm。
%
% 第一版我给所有行硬设 ht=7.4mm(自己拍的数),结果:
% 28 × 7.4 = 207.2mm,加标题区约 30mm + 说明约 25mm = 262mm
% 而版心只有 224mm ⇒ **必然溢出一页**,说明文字被推到第 3 页。
%
% ⇒ 改为 row{2-Z} 不设 ht,让内容撑开(与原 docx 一致);
% 表头行(R6/R14 分节标题)和签署行(R26/R27)单独给最小高度,
% 保证印章框有空间。
%
% ⚠️ 但 9pt + 0 上下内边距时行高只有 4.2mm,全表 28×4.2=118mm,
% 偏挤且不像正式表格。折中取 ht=6.0mm(≈ 一点留白),
% 28×6.0=168mm,加标题 30 + 说明 25 = 223mm,正好卡在 224mm 内。
% ⚠️ 若将来内容变多导致溢出,优先降这个值,不要动页边距。
% (注:这个 6.0mm 是写在下方 row{1-Z}={ht=6.0mm} 里的字面量,
% 不做成宏 —— 原因见第 4 节开头的说明。)
%======================================================================
% 4. 主表环境
%
% ⚠️⚠️ 合并单元格用**矩阵式语法**,绝对不能写在内容里用 \SetCell
%
% 现状(tabularray 2022A + 本表的 7 列 Q[c,m,wd=…] + colsep=1mm):
% 只要在单元格内容里写 \SetCell[c=n],编译立刻报
% ! Missing number, treated as zero.
% ! Illegal unit of measure (pt inserted.)
% \__dim_eval_end:
% 且**全部错误都定位到 \end{tblr}**,根本看不出是哪个格。
% 最小复现(已验证):
% \begin{tblr}{colspec={Q[c,m,wd=21mm] ...7列..., colsep=0.8mm}}
% \SetCell[c=2]{c} A & \SetCell[c=5]{l} B \\
% \end{tblr}
% 必崩;把同样的合并改写成下面的 cell{} 语法就 0 错。
%
% ✅ 正确写法:在 tblr 的选项里用
% cell{行}{列}={c=n}{对齐}
% 被合并掉的格在内容里**留空**(写 `& &`),不要写 {}。
%
% ⚠️ 另有一个坑(#23):纵向合并 \SetCell[r=n] 与横向 \SetCell[c=m]
% 不能出现在同一行,同样报上面那串错。本表因此完全不用纵向合并,
% 「其他费用」改成竖排文字 + 该列留空(视觉上等价)。
%======================================================================
\newenvironment{oucmeettable}{%
\par\begingroup\Mn
\begin{tblr}[label=none]{
colspec={
Q[c,m,wd=6.10mm] Q[c,m,wd=29.65mm] Q[c,m,wd=11.43mm] Q[c,m,wd=18.18mm]
Q[c,m,wd=29.65mm] Q[c,m,wd=3.00mm] Q[c,m,wd=40.10mm]
},
colsep=1.0mm, rowsep=0pt, stretch=0,
hline{1-Z}={0.5pt}, vline{1-Z}={0.5pt},
row{1-Z}={ht=5.6mm, font=\Mn, valign=m},
% ---- 合并规则(行号从 1 开始)----
% R1~R6 基本信息
cell{1}{1}={c=2}{c}, cell{1}{3}={c=5}{l},
cell{2}{1}={c=2}{c}, cell{2}{3}={c=2}{l}, cell{2}{5}={c=2}{c}, cell{2}{7}={c=1}{l},
cell{3}{1}={c=2}{c}, cell{3}{3}={c=5}{l},
cell{4}{1}={c=2}{c}, cell{4}{3}={c=5}{l},
cell{5}{1}={c=2}{c}, cell{5}{3}={c=5}{l},
cell{6}{1}={c=2}{c}, cell{6}{3}={c=5}{l},
% R7 收入预算标题(跨 7 列)
cell{7}{1}={c=7}{c},
% R8~R14 收入表
cell{8}{1}={c=2}{c}, cell{8}{3}={c=2}{c}, cell{8}{5}={c=2}{c}, cell{8}{7}={c=1}{c},
cell{9}{1}={c=2}{l}, cell{9}{3}={c=2}{l}, cell{9}{5}={c=2}{l}, cell{9}{7}={c=1}{l},
cell{10}{1}={c=2}{l}, cell{10}{3}={c=2}{l}, cell{10}{5}={c=2}{l}, cell{10}{7}={c=1}{l},
cell{11}{1}={c=2}{l}, cell{11}{3}={c=2}{l}, cell{11}{5}={c=2}{l}, cell{11}{7}={c=1}{l},
cell{12}{1}={c=2}{l}, cell{12}{3}={c=2}{l}, cell{12}{5}={c=2}{l}, cell{12}{7}={c=1}{l},
cell{13}{1}={c=2}{l}, cell{13}{3}={c=2}{l}, cell{13}{5}={c=2}{l}, cell{13}{7}={c=1}{l},
cell{14}{1}={c=2}{c}, cell{14}{3}={c=2}{l}, cell{14}{5}={c=2}{l}, cell{14}{7}={c=1}{l},
% R15 支出预算标题(跨 7 列)
cell{15}{1}={c=7}{c},
% R16~R20 支出表(3+2+2)
cell{16}{1}={c=3}{c}, cell{16}{4}={c=2}{c}, cell{16}{6}={c=2}{c},
cell{17}{1}={c=3}{l}, cell{17}{4}={c=2}{l}, cell{17}{6}={c=2}{l},
cell{18}{1}={c=3}{l}, cell{18}{4}={c=2}{l}, cell{18}{6}={c=2}{l},
cell{19}{1}={c=3}{l}, cell{19}{4}={c=2}{l}, cell{19}{6}={c=2}{l},
cell{20}{1}={c=3}{l}, cell{20}{4}={c=2}{l}, cell{20}{6}={c=2}{l},
% R21~R25 其他费用区(1+2+2+2)
% ★ 「其他费用」纵向合并 5 行,文字逐字竖排(不是旋转 90°)
% 写法:cell{21}{1}={r=5}{c} 声明纵向合并,
% 内容用 \shortstack{其\\他\\费\\用} 逐字堆叠。
% ⚠️ 不能用 \rotatebox{90} —— 那只是把整行文字旋转,
% 仍然是"横向一句转个角度",不是你要的上下排列(#24 修正)。
% ⚠️ 纵向合并也必须用 cell{} 矩阵语法。写成 \SetCell[r=5] 在
% 内容里,会与同行的 \SetCell[c=n] 冲突报 Missing number。
cell{21}{1}={r=5}{c},
cell{21}{2}={c=2}{l}, cell{21}{4}={c=2}{l}, cell{21}{6}={c=2}{l},
cell{22}{2}={c=2}{l}, cell{22}{4}={c=2}{l}, cell{22}{6}={c=2}{l},
cell{23}{2}={c=2}{l}, cell{23}{4}={c=2}{l}, cell{23}{6}={c=2}{l},
cell{24}{2}={c=2}{l}, cell{24}{4}={c=2}{l}, cell{24}{6}={c=2}{l},
cell{25}{2}={c=2}{l}, cell{25}{4}={c=2}{l}, cell{25}{6}={c=2}{l},
% R26 合计(3+2+2)
cell{26}{1}={c=3}{c}, cell{26}{4}={c=2}{l}, cell{26}{6}={c=2}{l},
% R27~R28 意见与签字(3+4)
cell{27}{1}={c=3}{l}, cell{27}{4}={c=4}{l},
cell{28}{1}={c=3}{l}, cell{28}{4}={c=4}{l},
}%
}{%
\end{tblr}\par\endgroup}
%======================================================================
% 5. 标题与页首
%======================================================================
% 左上角「附件1」小字(原 docx 无字号设置,随正文 9pt)
\newcommand{\oucmeetattach}[1]{%
\par\noindent{\Mn #1}\par\vspace*{4bp}}
% 标题:22pt 居中
\newcommand{\oucmeettitle}[1]{%
\par\begingroup\Mtw\heiti\centering #1\par\endgroup
\vspace*{8bp}}
% 填表日期 / 金额单位 那一行(左右分列,中间留白)
% 原 docx 用一串空格撑开,这里用 \hfill 更稳
\newcommand{\oucmeetdateline}[1]{%
\par\noindent{\Mn #1}\par\vspace*{4bp}}
%======================================================================
% 6. 表尾说明
%
% ⚠️ 悬挂缩进不能用 \leftmargin=2em —— 实测报
% Overfull \hbox (26.71pt too wide)
% 因为「1、」「2、」是**全角字符 + 顿号**,9pt 下实测宽约 2.4em,
% 比 2em 宽,标签凸出到左边界外。⇒ \labelwidth 要给足 2.5em。
%
% ⚠️⚠️ \itemindent 不能设负值!
% 第一版写 \itemindent=-2.5em 想让「1、」顶格,结果整段被拉到
% x=65.95,而版心左界是 79.40 —— **说明文字跑到版心外左侧 13.45bp**,
% 且只报一个 5.1pt 的 Overfull(因为 \hbox 宽度的计算没把左溢算进去)。
% ⇒ 正确做法:\leftmargin 与 \labelwidth 相等,\itemindent=0,
% 标签自然落在左边界,「1、」后面直接接正文。
% \labelwidth 要 ≥ 标签实际宽度(「1、」9pt 全角约 2.4em),给 2.5em。
% 见下方 \oucmeetnote 的说明。环境只负责字号与上间距,不再用 list。
\newenvironment{oucmeetnotes}{%
\par\vspace*{2bp}\begingroup\Mn
}{\par\endgroup}
%----------------------------------------------------------------------
% 单条说明
%
% ⚠️ 为什么不用 list 环境:list 的 \leftmargin / \itemindent 组合极易
% 把内容推出边界,而且**不报 Overfull**(踩过一次,文字跑到版心
% 左外 13.45bp,日志里只有个无关的 5.1pt)。
%
% ⚠️ 也不要用 \hangindent 悬挂缩进。「2、」后面直接接正文,续行不需要
% 缩进;相反,\hangindent 会让续行可用宽度变窄,第 2 条
% (50 字,两行)因此溢出 5.1pt。
% ⇒ 就是普通的 \noindent + 段落,什么都不设。
%
% ⚠️⚠️ \tolerance / \emergencystretch 必须在**段落开始前**设置。
% 写成 `\noindent` 之后再改无效(\noindent 已开段落,实测 5.1pt
% 的 Overfull 纹丝不动)。
% ⇒ 这里用 \sloppy,它在内部改 \tolerance 和 \emergencystretch,
% 且必须在 \par 之前调用 —— 下面 #1#2 之前就调了,有效。
%
% ⚠️ 为什么会溢出:第 2 条说明正好 50 字,9pt 下版心一行放 49 字,
% 第 50 字「现」挤到第二行后,第一行欠宽极小、第二行只有「金;」
% 两个字,TeX 判定这比"溢出 5.1pt"更难看。\sloppy 允许更松的断行,
% 两害相权取其轻。这是内容长度与版心宽度的巧合冲突,不是排版错误。
%----------------------------------------------------------------------
\newcommand{\oucmeetnote}[2]{%
\par\noindent\sloppy #1#2\par
\vspace*{1bp}}
\endinput