源: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 章示例 | 只抽骨架 |
143 lines
5.6 KiB
Bash
Executable File
143 lines
5.6 KiB
Bash
Executable File
#!/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" |