Files
ouc_latex_template/build.sh
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

143 lines
5.6 KiB
Bash
Executable File
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.
#!/usr/bin/env bash
# 在 WSL 内编译模板(WSL 原生版,无需 ssh/scp)
#
# 这套仓库的全部工作都在 WSL 内完成:编辑、编译、查看、提交、提工单。
# Windows 侧只通过 \\wsl$\<发行版>\... 这个 UNC 路径读取文件(只读浏览)。
#
# 用法:
# bash build.sh 特殊事项说明表 # 编译该目录下默认主文件
# bash build.sh 试卷 试卷题型示例 # 显式指定主文件(不带 .tex 也行)
# bash build.sh all # 全库所有主文件,逐个编译
# bash build.sh all 2>&1 | tee /tmp/v.log # 想留完整日志
#
# 可用环境变量:
# TEXBIN 默认 xelatex
# SILENT 非空时只打印结论行(给批量脚本用)
#
# ⚠️ 三条务必注意
#
# 1) **PDF 不要提交**。同源码编两次,页面一致但内嵌 CreationDate 不同,
# git 会认为文件变了(全库 13 个 PDF 全变 M)。提交前:
# git checkout -- '*.pdf'
# 或者用 git status 确认只 add 了源码。
#
# 2) **每个模板都要在它自己的目录里编**。.sty 里的 Path=../fonts/ 是相对
# 编译时工作目录解析的,从仓库根调用会找不到字体,报一堆
# "The font ... cannot be found"。本脚本已自动 cd,不用手动。
#
# 3) **两遍编译**。目录、页码、LastPage 跨引用要第二遍才稳定。
# 只编一遍会看到「第 ? 页」或者目录页码错位。
set -u
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEXBIN="${TEXBIN:-xelatex}"
SILENT="${SILENT:-}"
die() { echo "$1" >&2; exit 2; }
# 目录 → 主文件。目录名与主文件名不一致的必须在这里登记。
# 简历/ 下有 fontawesomesymbols-*.tex 等宏包片段,不能拿 ls 第一个 .tex 当主文件。
mainfile_of() {
case "$1" in
幻灯片) echo "slides" ;;
科技报告) echo "report" ;;
简历) echo "简历" ;;
试卷) echo "" ;; # 有两个主文件,必须显式指定
*) echo "$1" ;;
esac
}
# 编一个:$1=目录 $2=主文件名(不带 .tex)
build_one() {
local dir="$1" name="$2"
local log="/tmp/oucbuild.$$.log"
local log1="/tmp/oucbuild.$$.1.log"
# 第一遍:产出 .aux/.toc,允许非致命错误(TOC 还没生成时会警告)
( cd "$ROOT/$dir" && $TEXBIN -interaction=nonstopmode "$name.tex" ) > "$log1" 2>&1
local rc1=$?
# 有参考文献库时插入 bibtex(教材/ 等模板需要)。
# ⚠️ 判定依据是目录下存在 **任意 .bib** 且不是 biblatex 自动生成的
# "*-blx.bib"(那是 biblatex 的中间产物,不能当输入跑 bibtex)。
# biblatex 的 bcf 后端用 biber,但本库统一用 bibtex 后端(elegantbook
# 的默认),且仓库里的模板都用 bibtex,故这里只调 bibtex。
# bibtex 失败不中断构建 —— 下一遍 xelatex 会用旧的 .bbl,
# 参考文献可能不全,但正文和版式照常验证。
if ( cd "$ROOT/$dir" && ls *.bib 2>/dev/null | grep -qv -- '-blx\.bib$' ); then
( cd "$ROOT/$dir" && bibtex "$name" ) > "/tmp/oucbibtex.$$.log" 2>&1 || true
# bibtex 之后必须再跑一遍让引用编号落位
( cd "$ROOT/$dir" && $TEXBIN -interaction=nonstopmode "$name.tex" ) > "$log1" 2>&1
fi
# 最后一编:交叉引用稳定后的最终结果,统计以这一遍为准
( cd "$ROOT/$dir" && $TEXBIN -interaction=nonstopmode "$name.tex" ) > "$log" 2>&1
local rc2=$?
local err over under pages
err=$(grep -c '^!' "$log" 2>/dev/null || :)
over=$(grep -c 'Overfull' "$log" 2>/dev/null || :)
under=$(grep -c 'Underfull' "$log" 2>/dev/null || :)
pages=$(grep -o 'Output written on .*' "$log" 2>/dev/null | tail -1)
if [ -z "$SILENT" ]; then
echo "=== $dir / $name ==="
echo " error : $err"
echo " Overfull : $over"
echo " Underfull : $under"
echo " ${pages:-(无输出,见 $log)}"
else
printf '%-24s err=%-2s over=%-2s under=%-2s %s\n' \
"$dir" "$err" "$over" "$under" "$(echo "$pages" | sed 's/Output written on .*(\([0-9]*\) page.*/\1页/')"
fi
if [ "$rc2" -ne 0 ]; then
echo "--- 错误片段($log)---" >&2
grep -A6 '^!' "$log" 2>/dev/null | head -20 | sed 's/^/ /' >&2
# 保留失败日志,方便排查;成功时删掉
return "$rc2"
fi
rm -f "$log" "$log1"
return 0
}
[ $# -eq 0 ] && die "用法: bash build.sh <目录名|all> [主文件名]"
DIR="$1"; TEX="${2:-}"
#----------------------------------------------------------------------
# 全库模式:直接转给 verify_all.sh(那边有完整的 TARGETS 清单)
#----------------------------------------------------------------------
if [ "$DIR" = "all" ]; then
exec bash "$ROOT/verify_all.sh"
fi
#----------------------------------------------------------------------
# 单目录模式
#----------------------------------------------------------------------
if [ ! -d "$ROOT/$DIR" ]; then
echo "没有这个目录:$DIR" >&2
echo "可选:" >&2
( cd "$ROOT" && ls -d */ 2>/dev/null | sed 's|/$||' | sed 's/^/ /' ) >&2
exit 2
fi
if [ -z "$TEX" ]; then
TEX="$(mainfile_of "$DIR")"
if [ -z "$TEX" ]; then
echo "$DIR/ 有多个主文件,请显式指定:" >&2
( cd "$ROOT/$DIR" && ls *.tex 2>/dev/null | grep -v fontawesome | sed 's/\.tex$//' | sed 's/^/ /' ) >&2
echo "例: bash build.sh $DIR $(cd "$ROOT/$DIR" && ls *.tex 2>/dev/null | grep -v fontawesome | head -1 | sed 's/\.tex$//')" >&2
exit 2
fi
fi
# 允许用户写带 .tex 的
TEX="${TEX%.tex}"
if [ ! -f "$ROOT/$DIR/$TEX.tex" ]; then
echo "找不到 $DIR/$TEX.tex" >&2
exit 2
fi
build_one "$DIR" "$TEX"