Files
ouc_latex_template/教材/ouctextbook.sty
T
laoyang a4bfcac82a 新增《教材》LaTeX 模板,从 sre 教材抽取版式骨架 #25
源:D:\Yongquan\code\sre =《软件需求工程导论》完整教材
(13 章 + 5 附录,78 万字节,265 页 PDF,elegantbook 文档类)

## 新增文件

   教材/ouctextbook.sty    版式配置
   教材/elegantbook.cls    本地化过的文档类(改过字体)
   教材/教材.tex           示例正文(1 章 + 附录 + 参考文献)
   教材/reference.bib      参考文献库(虚构条目)
   教材/README.md          用法 + 踩坑记录

## 核心约束:字体的共用性

用户明确要求「注意字体的共用性」。原教材用 elegantbook 的
chinesefont=founder 档,加载方正四大字体:

   FZShuSong-Z01 / FZHei-B01 / FZKai-Z03 / FZFangSong-Z02

这些是**商业字体,不能随仓库分发**(#17 定下的规矩)。

### elegantbook 三档字体全都不通用

| 选项 | 实际字体 | 问题 |
|---|---|---|
| founder(默认) | 方正四大字体 | 商业字体,换机器就废 |
| ctexfont | 系统字体(Win=SimSun,macOS=PingFang) | 三平台三种样子 |
| nofont | 不设 | 不配置就报错 |

### 只写 nofont 还不够 —— cls 自身还硬编码字体

elegantbook.cls 第 176/184 行:

   \setmainfont{TeXGyreTermesX}[...]
   \setsansfont{texgyreheros}[...]

这两款在精简 TeX Live 上都没有,报 9 个错误连发:

   ! Package fontspec Error: The font "texgyreheros-regular" cannot be found

**实测:在 \documentclass 之后用 \setsansfont 覆盖无效** ——
类加载时字体族已注册失败,后面重新指定救不回来。

⇒ 所以本模板必须带一份**本地化的 cls**,改三处(都留了"本地化"注释):

| 位置 | 改动 |
|---|---|
| ~176 行 | TeXGyreTermesX → texgyretermes + Path=../fonts/tex-gyre-termes/ |
| ~184 行 | texgyreheros → OUCSans + Path=../fonts/ouc-sans/ |
| \maketitle | example-image → \ouc@coverblank |

这是本模板与仓库其他模板的**唯一结构性差异**:别的模板只要
\input{../fonts.tex},本模板还要那份改过字体的 cls。

## 踩到的三个坑

### 1. 封面不写 \cover{} 会插一张 "Image" 占位图

cls 第 1288~1300 行:
   \ifcsname @cover\endcsname ... \else
     \includegraphics[width=\linewidth]{example-image}   ← mwe 的灰底占位图

首页顶着 "Image" 两个大字。已把 else 分支换成空盒子。

### 2. 占位盒宽度不能是 0(这个坑耗了最久)

第一版 \rule{0pt}{0pt}(0 宽),立刻冒出:

   Overfull \hbox (5.00002pt too wide) in paragraph at lines 44--44

试过全部无效,溢出量死钉在 5.00002pt 不动:
  改 \hspace{2em} / \hspace{2.5em}     无效
  改 minipage 的 0.67/0.27\linewidth   无效
  改 tabular 列间距 @{}l@{}            无效
  改 \colorbox 的 \makebox[\hsize]     无效

根因:0 宽空盒让 \linewidth 在 titlepage 里与 \textwidth 差 5pt,
后续并排的 \parbox{0.8\textwidth} + 两个 minipage 整体超宽。

✅ 正解:给占位盒满宽
   \newcommand{\ouc@coverblank}{%
     \rule{0pt}{0pt}\hspace{\linewidth}\rule{0pt}{0pt}}
实测 0 Overfull。

### 3. 封面色带改不动(一行 \colorlet 的位置问题)

cls 第 345 行 `\colorlet{coverlinecolor}{second}` 在**类加载末尾**执行,
把 coverlinecolor 绑到 second 色(black 模式下就是黑)。

⇒ 导言区写 \colorlet 会被覆盖,实测色带仍是纯黑。
必须用 \AtBeginDocument 排到它后面。

## build.sh 补了 bibtex 支持

原来只跑两遍 xelatex,教材的参考文献出不来。
新增:目录下有 .bib(排除 *-blx.bib 这种中间产物)就插一次 bibtex,
再补一遍 xelatex,最后跑统计那一遍。

## 验证

   8 页  error 0  Overfull 0  Underfull 0
   封面色带 RGB(32,178,170) 青绿,实测 fill=(0.125,0.698,0.667)
   封面无 example-image 占位

字体(PDF 内嵌实测,全部来自仓库):
   OUCSerif-Regular / -Bold        中文正文
   OUCKai-Regular                  中文斜体(楷体代)
   TeXGyreTermes-Regular/-Bold/-Italic   西文
   LMMono10-Regular                \texttt 等宽(TeX Live 自带)

→ **无方正、无 SimSun/SimHei、无 Times New Roman**

全库 16/16 通过(新增第 16 项,已加入 verify_all.sh)。

⚠️ Windows 端本机未能编译:TeX Live 写字体缓存
(C:\texlive\2022\texmf-var)被安全策略拦截,属环境限制非模板问题。
模板只依赖 TeX Live 标准宏包与发行版自带字体;原教材本身就是在
Windows + TeX Live 2022 上编出来的。

## .gitignore 补充

新增 *-blx.bib、*.run.xml(biblatex 中间产物)、*/.preview/(预览图)。

## 与原教材的差异

| 项 | 原教材 | 本模板 | 原因 |
|---|---|---|---|
| 封面图 | cover.jpg | 无(纯色带) | 模板不附图片 |
| 字体 | 方正四大字体 | 仓库开源字体 | 商业字体不能分发 |
| 西文无衬线 | texgyreheros | OUC Sans | 仓库没有 Hero |
| 正文 | 13 章 265 页 | 1 章示例 | 只抽骨架 |
2026-11-01 08:06:22 +08:00

225 lines
10 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.
%======================================================================
% ouctextbook.sty —— 中国海洋大学 教材 版式包
%
% 从《软件需求工程导论》(sre 仓库) 的版式抽取而来,
% 底层是 elegantbook 文档类,保留其全部排版特性。
%
% 编译: xelatex 教材.tex (单次即可;有参考文献时 xelatex→bibtex→xelatex×2)
%
% ------------------------------------------------------------------
% ⚠️⚠️ 本模板与其他模板最大的不同:它是**文档类**不是宏包
%
% elegantbook 是 \documentclass 级别的类,不能 \usepackage。
% 所以本文件只提供**配置命令**,正文首行必须自己写
% \documentclass[...]{elegantbook},再 \usepackage{ouctextbook}。
%
% 顺序不能反:\documentclass 必须第一行。
% ------------------------------------------------------------------
%======================================================================
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{ouctextbook}[2026/11/01 v1.0 OUC textbook style]
%======================================================================
% 一、公共字体(本模板的核心约束)
%
% ⚠️⚠️ 为什么必须在 \documentclass 里写 chinesefont=nofont
%
% elegantbook 的 chinesefont 只有三档,全都不通用:
%
% founder(默认) 方正四大字体 FZShuSong-Z01 / FZHei-B01 /
% FZKai-Z03 / FZFangSong-Z02
% ⇒ **商业字体,不能随仓库分发**,换机器就废
% ctexfont ctex 按**系统**字体配(Windows=SimSun/SimHei,
% macOS=PingFang,Linux 视装了什么而定)
% ⇒ 同一份源码在三个平台排出三种样子
% nofont 不设字体,完全交给用户
% ⇒ 不额外配置就直接报错
%
% ⇒ 本模板用 nofont,然后在正文 \input{../fonts.tex} 接入仓库字体。
%
% ⚠️⚠️ 但只写 nofont 还不够 —— elegantbook.cls 自身还硬编码了两处字体:
%
% \setmainfont{TeXGyreTermesX}[...] % 西文衬线
% \setsansfont{texgyreheros}[...] % 西文无衬线
%
% 这两款在精简版 TeX Live 和很多机器上**都没有**,会直接报
% ! Package fontspec Error: The font "texgyreheros-regular"
% cannot be found
% 而且是 9 个错误连发,因为在类的每个字体档位上都会失败。
%
% 实测:在 \documentclass 之后用 \setsansfont 覆盖**无效** ——
% 类加载时该字体族已经注册失败,后面的重新指定救不回来。
%
% ⇒ 唯一可靠的做法是**本地化 cls 本身**:模板目录里带一份
% elegantbook.cls 副本,把那两处改成仓库字体:
% \setmainfont{texgyretermes}[Path=../fonts/tex-gyre-termes/]
% \setsansfont{OUCSans}[Path=../fonts/ouc-sans/]
% 改动处有 "本地化" 注释,便于将来对比上游版本。
%
% ⇒ 这也是本模板与仓库其他模板的**唯一结构性差异**:
% 别的模板只需要 \input{../fonts.tex},
% 本模板还需要那份改过字体的 cls 副本。
%
% ⚠️ fonts.tex 已用 \def 定义 \songti / \heiti / \kaiti / \fangsong,
% elegantbook 内部依赖 \heiti(用于 \cbfseries、章节标题),
% 所以这层依赖是通的。若哪天 fonts.tex 改了这几个命令的名字,
% 本模板的章节标题会立刻失去中文字体。
%======================================================================
\RequirePackage{etoolbox}
% 检查是否已加载 elegantbook 且用了 nofont。
% 若用户忘了写 nofont,给出明确提示而不是让方正字体报一堆乱码错误。
\AtBeginDocument{%
\ifcsname ELEGANT@chinesefont\endcsname
\ifdefstring{\ELEGANT@chinesefont}{nofont}{}{%
\PackageWarning{ouctextbook}{%
检测到 chinesefont 不是 nofont。请把文档类选项改为
\string\documentclass[lang=cn,newtx,10pt,black,chinesefont=nofont]{elegantbook}%
\MessageBreak
否则会加载方正商业字体或系统字体,失去跨平台一致性}%
}%
\fi
}
%======================================================================
% 二、标题编号层级
%
% 原教材主文件里的自定义,很有用,保留:
% 编号最多到第 4 级 章(1) → 节(1.1) → 小节(1.1.1) → 小小节(1.1.1.1)
% 第 5 级(\paragraph)不再进编号体系,改用「(1)(2)(3)」形式,
% 避免出现 3.1.1.1.1 这种过长编号。
%
% ⚠️ 这段必须在 \begin{document} 之前执行,用 \AtBeginDocument 会太晚
% (编号层级在导言区就被读取)。
%======================================================================
\setcounter{secnumdepth}{3}
\setcounter{tocdepth}{3}
\makeatletter
\newcounter{paranum}[subsubsection] % 随小小节自动重置
\renewcommand\paragraph[1]{%
\par\addvspace{0.9ex}%
\stepcounter{paranum}%
\noindent{\bfseries (\arabic{paranum})\,#1}%
\par\nobreak\addvspace{0.1ex}%
\@afterindentfalse\@afterheading}
\makeatother
%======================================================================
% 三、封面色带
%
% 原教材用青绿色 RGB(32,178,170),比 elegantbook 默认的蓝色沉稳,
% 与清华社的封面观感接近。保留。
%
% ⚠️⚠️ 必须在 \begin{document} 之后才能改 coverlinecolor
%
% elegantbook.cls 第 345 行有
% \colorlet{coverlinecolor}{second}
% 这行在**类加载末尾**执行,把 coverlinecolor 绑到 "second" 色上
% (color=black 选项下 second 就是黑色)。
%
% ⇒ 在导言区写 \colorlet{coverlinecolor}{...} 会被它覆盖,
% 实测封面色带仍是纯黑,改了半天没反应。
% 必须用 \AtBeginDocument 排到它后面。
%======================================================================
\definecolor{ouccoverlinecolor}{RGB}{32,178,170}
\AtBeginDocument{%
\colorlet{coverlinecolor}{ouccoverlinecolor}%
}
% 需要临时换别的色时,在正文里调这个(不调则用上面的青绿)
\newcommand{\oucSetCoverColor}[1]{%
\colorlet{coverlinecolor}{#1}%
}
%======================================================================
% 四、图形与表格
%
% 原教材用到的宏包,逐个说明为什么需要:
% array \makecell(表格内换行)
% cprotect \verb 等脆弱命令进表格/标题
% graphicx 插图
% float [H] 强制定位(教材排版要求图跟文字,不能漂走)
% longtable 跨页表格
%======================================================================
\RequirePackage{array}
\RequirePackage{cprotect}
\RequirePackage{graphicx}
\RequirePackage{float}
\RequirePackage{longtable}
% TikZ 绘图库(原教材的流程图/关系图用这些)
\RequirePackage{tikz}
\usetikzlibrary{
shapes.geometric, shapes.misc,
arrows.meta, arrows,
decorations.pathreplacing, decorations.markings,
positioning, calc, fit, backgrounds
}
%======================================================================
% 五、断行宽容度
%
% ⚠️ 教材正文长、中文多、行内夹英文术语和公式,默认 \tolerance
% 会产生大量 Overfull(原教材明确加过这一条)。
% 2em 是原教材实测的值,既消掉大部分告警,又不会排出稀疏的行。
%======================================================================
\setlength{\emergencystretch}{2em}
%======================================================================
% 六、常用排版命令
%======================================================================
% 表格里的色块占位(原教材 \ccr 的用途:图例色块)
\newcommand{\ouccolorcell}[1]{%
\makecell{{\color{#1}\rule{1cm}{1cm}}}}
%======================================================================
% 七、章节末的标准小节
%
% 原教材每章固定以「本章小结」+「习题」收尾。
% 做成命令,避免各章写法不一致。
%======================================================================
\newcommand{\oucsummary}{\section{本章小结}}
\newcommand{\oucexercises}{\section{习题}}
%======================================================================
% 八、封面:无封面图时不插 example-image
%
% ⚠️⚠️ elegantbook 在没写 \cover{} 时会插入 mwe 宏包的 example-image,
% 也就是**一张印着 "Image" 字样的灰底占位图**,铺满首页。
% 源码(cls 第 1288~1300 行):
% \ifcsname @cover\endcsname
% \includegraphics[width=\linewidth]{\@cover}
% \else
% \includegraphics[width=\linewidth]{example-image} ← 就是这个
% \fi
% 交付给别人的模板首页顶着 "Image" 两个大字,不合适。
%
% ⇒ 解法在 cls 里,不在本文件:本地化版本的 elegantbook.cls 已把
% 那两处 example-image 换成了 \ouc@coverblank(一个占位盒子)。
% 这样不写 \cover{} 时封面只有色带和文字,没有占位图。
%
% ⚠️⚠️ 占位盒的宽度**不能是 0**。
%
% 第一版写成 \rule{0pt}{0pt}(0 宽),立刻冒出
% Overfull \hbox (5.00002pt too wide) in paragraph at lines 44--44
% 而且它只出现在 \maketitle 那一行,改 \hspace{2em}、\hspace{2.5em}、
% minipage 的 0.67/0.27\linewidth、tabular 的列间距,**全部无效**,
% 溢出量死钉在 5.00002pt 不动。
%
% 根因:0 宽空盒让 \linewidth 在 titlepage 里的计算与 \textwidth
% 差了 5pt,后续并排的 \parbox{0.8\textwidth} + 两个 minipage
% 就整体超宽。
%
% ✅ 正解:给占位盒一个满宽
% \newcommand{\ouc@coverblank}{%
% \rule{0pt}{0pt}\hspace{\linewidth}\rule{0pt}{0pt}}
% 实测 0 Overfull。
%
% ⚠️ 不要试图在本文件里用 \ifcsname 覆盖 \@cover 来绕开 —— 实测
% \@cover 即使定义成空串也会走 \includegraphics{} 分支报错。
% 改 cls 是唯一干净的办法。
%======================================================================
\endinput