一、项目经验整块右移(简历/awesome-source-cv.cls)
\rightcolumnlength 算早了:geometry 的 1.5cm 边距要到导言区结束才生效,
cls 里那句 \setlength 读到 article 默认 \textwidth=345pt,算出 249.87pt,
比应有的 417.02pt 少 167.15pt。longtable 把窄表居中排,整块右移 83.6pt。
修法:计算挪进 \AtBeginDocument 并减去 | 竖线的 \arrayrulewidth;
R 列由 >{\hfill}m{2.5cm} 改为 >{\raggedright\arraybackslash}p{2.5cm}。
日期左边缘 145.1 -> 48.5pt,与其余章节严格对齐。
这类错误零 error 零 warning,只能量 PDF 坐标才能发现。
二、西文换 Cochineal(fonts/cochineal/ + fonts.tex + 简历 cls)
中文思源宋体配 Termes 时西文笔画偏细,中英混排会「陷进去」。
Cochineal 宽 225.42pt vs Termes 229.68pt,只窄 1.9%,断行位置基本不动;
Pagella/XCharter 宽 7~11%,会把全库断行推着变,故不选。
字体 920KB 入 fonts/cochineal/(SIL OFL 1.1,附 OFL.txt),
fonts.tex 新增 \oucUseCochineal 开关,只简历调用,其余 10 个模板不受影响。
三、换字体后暴露的真实缺陷
西文窄 1.9% 让断点整体前移,ICIEA 那条论文冲出 88.49pt(文字顶到纸边)。
根因是中文紧跟英文的位置不是断开点,整串被当长单词。
加 \emergencystretch=3em 解决。
四、课程执行大纲漏网占位符(ouccourse.sty)
××学部××学院 硬编码在 \ccschool 里,#11 只扫了 .tex 没扫 .sty 所以漏了。
拆成 \ccdepartment / \cccollege 两个名称宏,默认「信息科学与工程学部 /
计算机科学与技术学院」。重编后全文 × 归零。
「代码库 PDF 没更新」经核实不成立:本地/远程 HEAD 均为 8984b93,
从服务器下载 8 个 PDF 逐字节比对全部一致,简历.pdf 3 页 0 个 ×。
验证:简历 3 页 0 error/0 Overfull/0 Underfull;全库 12 项编译回归与基线一致。
fonts/ —— 模板库公共字体目录
本目录是中国海洋大学 LaTeX 模板库唯一的一份字体来源。仓库里十一个模板全部通过
根目录的 ../fonts.tex 取字体,不再有任何模板自带字体、也不依赖
操作系统里装了什么字体。换一台机器、换一个操作系统(Windows / macOS / Linux /
Overleaf),编译出来的样子都一样。
fonts/
├── ouc-serif/ OUC Serif 宋体(衬线正文) 2 个,9.7 MB
├── ouc-sans/ OUC Sans 黑体(无衬线标题) 2 个,7.0 MB
├── ouc-kai/ OUC Kai 楷体 2 个,7.0 MB
├── zhuque-fangsong/ Zhuque Fangsong 仿宋 1 个,8.4 MB
├── tex-gyre-termes/ TeX Gyre Termes 西文 Times 等价 4 个,550 KB
├── cochineal/ Cochineal 西文 Minion 风格 4 个,920 KB
├── harmonyos-sans/ HarmonyOS Sans 鸿蒙(中英各一档) 6 个,16 MB
└── fontawesome/ FontAwesome 简历模板的图标符号 1 个,130 KB
合计约 50 MB(改造前是 141 MB:幻灯片 49 MB + 科技报告 92 MB 各自带一份, 外加两套重复的字体。现在全库只有这一份,省掉 65%)。
一、为什么要把字体打进仓库
原先七个模板是两种做法:
| 做法 | 模板 | 问题 |
|---|---|---|
| 各自带一份字体 | 幻灯片/(鸿蒙 18 个文件 49 MB)、科技报告/(鸿蒙 + 方正 5 款 92 MB) |
两份重复;字体版本一旦不同步,两个模板的样子就会不一致 |
| 直接用系统字体 | 其余五个(SimSun / SimHei / FangSong / KaiTi / Times New Roman) | 换台机器(Linux、macOS、精简版 Windows、Overleaf)直接编不出来;而且这些字体不允许随仓库分发 |
统一成一份公共字体后:
- 编译结果可复现 —— 与操作系统、与是否装过某款字体无关。
- 授权干净 —— 全库不含任何商业字体,见第四节。
- 体积可控 —— 只保留实际用到的字重与字符集。
二、各字体的来源与授权
全部为开源或官方允许免费商用的字体。逐项如下。
1. OUC Serif / OUC Sans / OUC Kai —— 思源系列与霞鹜文楷的子集
| 上游字体 | 上游作者 | 上游授权 | 字符集 | |
|---|---|---|---|---|
| OUC Serif | 思源宋体 SC(Source Han Serif SC) | Adobe | SIL OFL 1.1 | GB2312,7555 字 |
| OUC Sans | 思源黑体 SC(Source Han Sans SC) | Adobe | SIL OFL 1.1 | GB2312,7555 字 |
| OUC Kai | 霞鹜文楷(LXGW WenKai) | LXGW | SIL OFL 1.1 | GB2312,7557 字 |
为什么改名叫 OUC Serif / OUC Sans / OUC Kai? SIL OFL 1.1 第 3 条规定:修改版不得使用上游的 Reserved Font Name。 思源系列的 RFN 是
Source,霞鹜的 RFN 含LXGW。裁剪字符集属于 "修改版",因此必须改名,且不得暗示原作者为本仓库背书。 改名同时改了 CFF 层的fontNames/FamilyName/FullName—— 只改 name 表是不够的,PDF 里内嵌的字体名取的是 CFF 层。 字体 name 表的 13/14 号记录保留了完整 OFL 声明与"本字体为子集、 与上游作者无关"的免责说明。
裁剪范围:保留 GB2312 全集(6763 汉字)+ 常用标点 + 拉丁字母 + 希腊字母
- 数学符号,约 7555 个字符。不保留 GBK 扩展区与生僻字。
⚠️ 如果将来正文要用 GB2312 之外的生僻字(例如某些人名、地名用字), 会显示成空白或方框。此时需要重新生成子集,把该字加进去。 生成脚本见第七节。
2. Zhuque Fangsong —— 朱雀仿宋
| 上游 | 朱雀仿宋(TrionesType / Zhuque Fangsong) |
| 授权 | SIL OFL 1.1 |
| 字符集 | 12882 字符,GB2312 全集覆盖 |
| 版本 | 0.212(technical preview) |
未做裁剪(原文件 8.4 MB 已经够小,且保留全集更安全)。 只有常规一档,没有粗体 —— 需要粗体的地方请改用黑体或宋体。
3. TeX Gyre Termes —— 西文 Times 等价字体
| 上游 | TeX Gyre Termes(GUST 字体项目) |
| 授权 | GUST Font License(GPL + 字体嵌入例外) |
| 特性 | 与 Times New Roman 逐字度量兼容 |
用它是为了保证中文模板里原有的西文断行、表格列宽、行内公式对齐不漂移。 实测同一段英文,Termes 与 Times New Roman 的逐词宽度差在 0.03pt 量级。
全库默认西文字体就是 Termes(fonts.tex 的 \setmainfont)。除简历外的
10 个模板都用它。
3b. Cochineal —— 西文 Minion 风格(简历专用)
| 上游 | Cochineal(Michael Sharpe,据 Minion 设计) |
| 授权 | SIL OFL 1.1,可随仓库分发(cochineal/OFL.txt) |
| 特性 | 笔画比 Times 饱满,与思源宋体灰度更接近 |
为什么加这一款:中文用思源宋体(中等粗细),Termes 是 Times 的度量兼容替代, 笔画偏细偏紧,中英混排时西文那一段视觉上会「陷进去」。Cochineal 笔画更饱满。
为什么只有简历用:它的宽度与 Termes 只差 1.9%(225.42 vs 229.68pt @10pt),
断行位置基本不动,所以换过去风险最小。其余候选宽 2.2%~10.8%,会把全库的表格列宽
和断行推着变(数据见 字体 一节末尾的对照表)。
⚠️ 不是全局生效。fonts.tex 里只做了声明:
\oucUseCochineal % 调它才切;不调就还是 Termes
目前只有 简历/awesome-source-cv.cls 在 \AtBeginDocument 里调用。
度量对照(10pt 下同一句英文的排印宽度,供将来换字体时参考):
| 字体 | 样句宽 | 相对 Termes | x 高 | cap 高 |
|---|---|---|---|---|
| Termes | 229.68pt | 100.0% | 0.450 | 0.662 |
| Cochineal | 225.42pt | 98.1% | 0.433 | 0.649 |
| Libertinus Serif | 234.62pt | 102.2% | 0.433 | 0.649 |
| XCharter | 246.69pt | 107.4% | 0.481 | 0.671 |
| TeX Gyre Pagella | 254.40pt | 110.8% | 0.472 | 0.695 |
4. HarmonyOS Sans —— 鸿蒙字体
| 上游 | HarmonyOS Sans(华为) |
| 授权 | 华为官方声明允许免费商用(随 HarmonyOS 开发者文档发布) |
| 保留原因 | 幻灯片/ 与 科技报告/ 原有观感就是鸿蒙,换成思源会明显改变这两个模板的视觉 |
只保留实际用到的 6 个文件:HarmonyOS_Sans_{Regular,Bold}(西文)、
HarmonyOS_Sans_{Regular,Bold}_Italic、HarmonyOS_Sans_SC_{Regular,Bold}(中文)。
原目录里的 Black / Light / Medium / Thin 等 12 个字重全库无人引用,已删除。
⚠️ 鸿蒙字体的文件名用下划线分隔(
HarmonyOS_Sans_SC_Regular.ttf), 不是族名-字重格式。所以在 fontspec 里不能写*-Regular通配, 必须显式给完整文件名。
5. FontAwesome —— 简历模板的图标符号
| 上游 | Font Awesome 4.x(原项目 Fonticons, Inc.) |
| 授权 | SIL OFL 1.1(字体本身)/ CC BY 4.0(图标设计) |
| 保留原因 | 简历/ 模板的联系方式行用 \smartphone / \email / \address 等命令,每个前缀都要印一个图标 |
| 版本 | 与 TeX Live 2022 内置的那份逐字节相同(md5 9f6ff7d7cfd6f76d7a9c242bdd48c144) |
只有 FontAwesome.otf 一个文件(130 KB)。TeX Live 里本来就带这份字体,之所以
还是拷进仓库,是为了守住「字体单一来源」这个约定 —— 否则 简历/ 就成了唯一一个
字体来自 TeX Live 安装的模板,换一台没装 fontawesome 宏包用字体的机器就编不出来。
⚠️ 引用它的不是
fonts.tex,而是简历/fontawesome.sty里的\newfontfamily{\FA}[Path=../fonts/fontawesome/,Extension=.otf]{FontAwesome}。 原因:\FA是在\RequirePackage{fontawesome}时就被定义好的(早于.cls后面所有语句),awesome-source-cv.cls里再声明一次会报Font \FA already defined;所以只能改fontawesome.sty本体。
三、字体是怎么被调用的
所有模板都只做一件事:在导言区 \input{../fonts.tex}(幻灯片与科技报告因为
目录层级不同,路径略作调整)。fonts.tex 负责:
- 加载 fontspec / xeCJK;
- 注册默认族与
\songti/\heiti/\kaiti/\fangsong/\kaishu/\tnr/\tnrb/\hm/\hmlatin; - 把
×—□○√等「长得像西文、其实该走中文」的符号 归入 CJK 字符类(详见该文件的注释); - 提供
\ouclinespread行距工具。
各模板自己不再写任何 \setCJKmainfont / \setmainfont。
⚠️ 三个容易踩的坑(都写在 fonts.tex 的注释里了)
-
必须保留
fontset=none。\documentclass[UTF8,fontset=none]{ctexart}。不写的话 ctex 会先按系统字体 配一套(Microsoft YaHei / SimSun / FangSong),再被覆盖,既浪费又有平台 依赖。试卷/ouckey.sty因为继承 ctexart 默认 fontset,所以 fonts.tex 里 用\setCJKfamilyfont+\def而不是\newCJKfontfamily—— 后者是 LaTeX3 的\NewDocumentCommand,命令已存在时会报! LaTeX3 Error: Control sequence \songti already defined.并中断编译。 -
Path=是相对编译时的工作目录,不是相对 .sty 文件。 模板目录下xelatex xxx.tex,所以写../fonts/...。 幻灯片与科技报告的主题文件虽然在子目录里,路径仍按工作目录写../fonts/。 -
Path=不能写宏。 它是 fontspec 的参数,必须给字面字符串。
四、授权合规自查
- 全库不含方正、SimSun、SimHei、KaiTi、FangSong、Times New Roman、 Microsoft YaHei 等不可分发的商业字体。
- 思源系列与霞鹜文楷的子集已改名,符合 OFL 1.1 §3(Reserved Font Name)。
- 子集保留了 OFL 全文(name 表 13/14 号记录),符合 OFL 1.1 §1(随附授权)。
- 改名后的 name 表写明"本字体为子集,与上游作者无关",未暗示背书。
- TeX Gyre Termes 为 GUST Font License,允许随文档分发。
- HarmonyOS Sans 为华为官方允许免费商用。
- 本目录的 LICENSE 汇总了各字体的授权原文出处。
注意:OFL 要求"以 OFL 字体排版产生的文档"不受 OFL 约束 (§5 文档本身不因此被感染)。所以用本模板编译出来的论文、报告、 试卷,版权完全属于作者,不受这些字体授权的影响。
五、体积
改造前(字体分散在各模板目录)
幻灯片/fonts/ 49 MB
科技报告/font/ 92 MB
合计 141 MB
改造后(统一到 fonts/)
ouc-serif 9.7 MB
ouc-sans 7.0 MB
ouc-kai 7.0 MB
zhuque-fangsong 8.4 MB
tex-gyre-termes 0.55 MB
cochineal 0.92 MB
harmonyos-sans 16.4 MB
合计 50 MB (省 65%)
六、当前各模板的字体对应关系
| 模板 | 中文正文 | 中文标题/强调 | 西文 |
|---|---|---|---|
试卷归档封面/ |
OUC Serif | OUC Sans | TeX Gyre Termes |
年度考核登记表/ |
OUC Serif | OUC Sans | TeX Gyre Termes |
劳务发放说明表/ |
OUC Serif | OUC Sans | TeX Gyre Termes |
课程执行大纲/ |
OUC Serif | OUC Sans | TeX Gyre Termes |
试卷/ |
OUC Serif / OUC Kai | OUC Sans | TeX Gyre Termes |
简历/ |
OUC Serif / OUC Kai | OUC Serif Bold | Cochineal |
幻灯片/ |
HarmonyOS Sans SC | HarmonyOS Sans SC Bold | HarmonyOS Sans |
科技报告/ |
OUC Serif | OUC Serif Bold / OUC Kai | HarmonyOS Sans |
「改造前」用的是:SimSun / SimHei / KaiTi / FangSong / Times New Roman(前五个模板), 鸿蒙 + 方正书宋/小标宋/楷体/仿宋/中等线(科技报告)。
七、如何重新生成子集
如果需要把 GB2312 之外的字加进来,或上游字体发了新版:
# 依赖:pip install fonttools
python -c "import fontTools; print(fontTools.version)"
# 1) 裁剪(name_IDs 必须给 '*',否则会丢掉 OFL 声明的 13/14 号记录)
python - <<'PY'
from fontTools import subset
opts = subset.Options()
opts.name_IDs = ['*'] # 关键:默认只取 0..6,会丢授权声明
opts.name_languages = ['*']
opts.notdef_outline = True
opts.recalc_bounds = True
opts.layout_features = ['*']
font = subset.load_font('上游字体.otf', opts)
subsetter = subset.Subsetter(options=opts)
subsetter.populate(text=open('keep_gb2312.txt', encoding='utf-8').read())
subsetter.subset(font)
subset.save_font(font, '裁剪后.otf', opts)
PY
# 2) 改 CFF 层名字(只改 name 表不够!PDF 内嵌名取的是 CFF 层)
python - <<'PY'
from fontTools.ttLib import TTFont
f = TTFont('裁剪后.otf')
cff = f['CFF '].cff
cff.fontNames[0] = 'OUCSerif-Regular'
td = cff.topDictIndex[0]
td.FamilyName = 'OUC Serif'
td.FullName = 'OUC Serif Regular'
# name 表同步改,并补 13/14 号授权记录与免责声明
f.save('OUCSerif-Regular.otf')
PY
改完务必验证 PDF 里内嵌的确实是新名字:
import pymupdf
d = pymupdf.open('输出.pdf')
print({f[3] for p in d for f in p.get_fonts()})
# 期望看到 OUCSerif-Regular / OUCSans-Regular,而不是 SourceHanSerifSC
八、字体清单速查
| 文件 | 内部族名 | 字符数 | 大小 |
|---|---|---|---|
ouc-serif/OUCSerif-Regular.otf |
OUC Serif | 7555 | 4.6 MB |
ouc-serif/OUCSerif-Bold.otf |
OUC Serif | 7555 | 4.8 MB |
ouc-sans/OUCSans-Regular.otf |
OUC Sans | 7555 | 3.4 MB |
ouc-sans/OUCSans-Bold.otf |
OUC Sans | 7555 | 3.4 MB |
ouc-kai/OUCKai-Regular.ttf |
OUC Kai | 7557 | 3.4 MB |
ouc-kai/OUCKai-Bold.ttf |
OUC Kai | 7557 | 3.4 MB |
zhuque-fangsong/ZhuqueFangsong-Regular.ttf |
Zhuque Fangsong | 12882 | 8.4 MB |
tex-gyre-termes/texgyretermes-regular.otf |
TeX Gyre Termes | 1053 | 141 KB |
tex-gyre-termes/texgyretermes-bold.otf |
TeX Gyre Termes | 1053 | 139 KB |
tex-gyre-termes/texgyretermes-italic.otf |
TeX Gyre Termes | 1053 | 141 KB |
tex-gyre-termes/texgyretermes-bolditalic.otf |
TeX Gyre Termes | 1053 | 144 KB |
cochineal/Cochineal-Roman.otf |
Cochineal | 1237 | 227 KB |
cochineal/Cochineal-Bold.otf |
Cochineal | 1255 | 228 KB |
cochineal/Cochineal-Italic.otf |
Cochineal | 1186 | 228 KB |
cochineal/Cochineal-BoldItalic.otf |
Cochineal | 1198 | 219 KB |
harmonyos-sans/HarmonyOS_Sans_Regular.ttf |
HarmonyOS Sans | 874 | 147 KB |
harmonyos-sans/HarmonyOS_Sans_Bold.ttf |
HarmonyOS Sans | 874 | 146 KB |
harmonyos-sans/HarmonyOS_Sans_Regular_Italic.ttf |
HarmonyOS Sans | 807 | 141 KB |
harmonyos-sans/HarmonyOS_Sans_Bold_Italic.ttf |
HarmonyOS Sans | 807 | 141 KB |
harmonyos-sans/HarmonyOS_Sans_SC_Regular.ttf |
HarmonyOS Sans SC | 29063 | 8.3 MB |
harmonyos-sans/HarmonyOS_Sans_SC_Bold.ttf |
HarmonyOS Sans SC | 29063 | 8.2 MB |