diff --git a/README.md b/README.md index e7f471b..d0ba527 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,64 @@ 3. 可以安装其他的IDE工具进行Tex文件的编写和修改,推荐使用Visual Studio Code。 4. 编译时,请选择XeLaTex,有时候,可能需要连续编译两次,以得到正确的页码和图表编号。 +# 分支与编译器版本 + +本仓库按 **TeX Live 年份**组织分支,规则很简单:**分支名是哪一年,就说明该分支可以用那一年的 TeX Live 直接编译通过**。 + +| 分支 | 对应 TeX Live | beamer 版本 | 说明 | +|------|---------------|-------------|------| +| `main` | 最新版 | 最新 | 始终指向最新的年份分支(当前等于 `2026`) | +| `2026` | TeX Live 2026 | 3.77 | 无需改动源码即可编译通过 | +| `2025` | TeX Live 2025 | 3.72 | 无需改动源码即可编译通过 | +| `2024` | TeX Live 2024 | 3.71 | 无需改动源码即可编译通过(PDF 体积说明见下) | +| `2023` | TeX Live 2023 | 3.69 | 适配 beamer 3.69 的 frametitle 变更,并修复 allcaps 选项 | +| `2022` | TeX Live 2022 | 3.66 | 保留早期 beamer 的写法(旧版 frametitle 补丁) | + +### 两点说明 + +**一、各分支的 PDF 都是用对应年份的 TeX Live 编译出来的。** +也就是说,`2023` 分支里的 `slides.pdf` 出自 TeX Live 2023, +`2026` 分支里的出自 TeX Live 2026,以此类推。功能上各分支内容一致, +差别只在于适配各自 beamer 版本的源码写法。 + +**二、`2024` 分支的 PDF 体积明显偏大(约 9.7 MB,其他年份约 2.4 MB)。** +这是 TeX Live 2024 的 `xdvipdfmx` 一度不做跨页图像去重导致的, +从 TeX Live 2025 起恢复正常。**页面渲染内容与其他年份完全一致**,不影响使用。 + +## 我该用哪个分支 + +- **想用最新版本**:直接用 `main` 分支,并配合**最新版 TeX Live**。 +- **本机已经装了某个特定年份的 TeX Live**:切到**同名的那个分支**即可。 + +```bash +# 例如本机是 TeX Live 2023,就用 2023 分支 +git clone -b 2023 https://code.yangyq.net/laoyang/ouc-slide-latex-template.git + +# 已经克隆过的仓库,可以这样切换 +git checkout 2023 +``` + +简单说:**编译器是哪一年的,就用哪一年的分支**,不需要自己去改模板代码。 + +## 为什么要这样分 + +beamer 的内部实现会随 TeX Live 版本变化,而模板中对内部宏的补丁(`\patchcmd`)会因此失效。最典型的一次是: + +- **beamer 3.69(TeX Live 2023)** 起,`\beamer@@frametitle` 改用 `\beamer@insertframetitlecontinuation`,使得面向旧版写法的补丁匹配失败,报 `Patching original frame title failed`。 +- 模板现已加入**版本自动判别**(依据 `\beamer@insertframetitlecontinuation` 是否存在,判断逻辑写在文件顶层),因此 `2023` 及之后的各年份分支**不需要再改动源码**。 +- `2022` 分支保留原始写法,只保证在 TeX Live 2022(beamer 3.66)下编译通过。 + +## 维护约定 + +每当有新的 TeX Live 年份发布时: + +1. 在新版本的 TeX Live 下,用 **XeLaTeX 编译当前的 `main`**; +2. 如果编译不通过,进行修复,注意保持向后兼容,不要破坏旧年份分支; +3. 修复通过后,新建一个**以该年份命名**的分支并提交; +4. 把该年份分支**快进合并到 `main`**,让 `main` 始终是最新版本。 + +> 附注:不同年份的 TeX Live 生成的 PDF 在体积上可能略有差异(例如 TeX Live 2024 的 xdvipdfmx 曾一度不做跨页图像去重,导致成品偏大约 4 倍),但**页面渲染内容是一致的**,不影响使用。 + # 元素介绍 ## 基本信息 Tex文件的开头,是基本信息部分,可以根据需要添加`\title`{标题}、`\subtitle`{子标题}、`\author`{作者}、`\date`{\today}、`\institute`{所属机构}。 @@ -50,6 +108,74 @@ Tex文件的开头,是基本信息部分,可以根据需要添加`\title`{ 首页和结尾页,使用的是基本信息中的数据,而目录页的内容,将根据`\section`进行填充。 +## 目录页样式(自适应方框) + +目录页默认使用**自适应方框**样式:每一个一级标题(`\section`)被放进一个独立的方框里,**方框的高度、以及方框内文字的字号,都会根据一级标题的数目自动计算**,并且方框会自动均分、铺满整个目录页。章节少时方框又高又大、字号醒目;章节多时方框自动变矮、字号自动缩小,不需要手工调任何一个数字。 + +方框内的文字**左对齐**,左侧只留一点点缩进,读起来更像一份正常的目录。 + +![默认目录页样式](/figs/fig09.png) + +### 配色:默认海大红,蓝色可选 + +目录页默认使用**海大红**(`oucred`)——和各章节标题页的颜色保持一致。 + +这是因为目录页上「目录」二字本身是**海大蓝**的,如果目录项再用蓝色,整页就分不出层次。用海大红正好和章节标题页呼应。 + +如果就喜欢蓝色,改一行即可切回海大蓝: + +``` +\toctheme{red} % 默认:海大红,与各章节标题页同色 +\toctheme{blue} % 海大蓝,与目录页标题「目录」同色 +``` + +![蓝色配色](/figs/fig13.png) + +### 三种方框样式 + +在导言区用一行命令即可切换,样式与配色可以自由组合: + +``` +\settocstyle{box} % 默认:浅色底 + 圆角框 + 圆形序号 +\settocstyle{solid} % 实心色底 + 白色文字 +\settocstyle{bar} % 白底 + 左侧粗色条 +``` + +![实心样式](/figs/fig10.png) +![左侧色条样式](/figs/fig11.png) + +章节较多时(下面这张是 9 个一级标题),方框和字号会自动压缩: + +![章节较多时自动压缩](/figs/fig12.png) + +### 可调参数 + +所有参数都可以在导言区用 `\setlength` 覆盖,一般**不需要改**: + +| 参数 | 默认值 | 含义 | +|------|--------|------| +| `\tocareamargin` | `52pt` | 目录区上下预留的高度(自动模式下从 `\textheight` 中扣除) | +| `\tocareaheight` | `0pt` | 直接指定目录区高度;`0pt` 表示自动 | +| `\tocboxgap` | `10pt` | 方框之间的理想间距(空间紧张时会自动压缩) | +| `\tocboxgapmin` | `2pt` | 方框之间允许的最小间距 | +| `\tocboxmaxheight` | `110pt` | 单个方框的最大高度 | +| `\tocboxleftpad` | `2.5pt` | 方框内文字距左边框的距离(只留一点点) | +| `\tocfontmax` | `26pt` | 方框内文字的最大字号 | +| `\tocfontwarn` | `8pt` | 字号低于该值时会给出警告 | + +例如,希望方框之间更紧凑一些: + +``` +\setlength{\tocboxgap}{6pt} +``` + +### 几点说明 + +1. **目录页需要连续编译两次**:章节数目是从上一次编译生成的 `.toc` 文件里读出来的。第一次编译时 `.toc` 还是空的,目录页会是空白,再编译一次即可正常。 +2. 方框的序号和标题是**超链接**,在 PDF 里点击会直接跳到对应章节。 +3. 一级标题**太多时(大约 9 个以上)**,方框和字号会变得偏小,编译日志里会有 `oucslide Warning` 提示。这种情况下建议合并章节,或者把目录页改回普通的列表样式。 +4. 如果确实想换回旧版那种简洁的列表目录,把 `\makeoutline` 里的 `\ouc@toc@body` 换回 `\tableofcontents[hideallsubsections]` 即可(旧的模板定义仍保留在 `beamerouterthemeOUCSlide.sty` 中)。 + ## 使用分节 本模板使用分节进行整体规划,章节共三级,对应的关键字分别是: diff --git a/figs/fig09.png b/figs/fig09.png new file mode 100644 index 0000000..6ed7f3a Binary files /dev/null and b/figs/fig09.png differ diff --git a/figs/fig10.png b/figs/fig10.png new file mode 100644 index 0000000..cbae1d8 Binary files /dev/null and b/figs/fig10.png differ diff --git a/figs/fig11.png b/figs/fig11.png new file mode 100644 index 0000000..f4f7302 Binary files /dev/null and b/figs/fig11.png differ diff --git a/figs/fig12.png b/figs/fig12.png new file mode 100644 index 0000000..a905e8d Binary files /dev/null and b/figs/fig12.png differ diff --git a/figs/fig13.png b/figs/fig13.png new file mode 100644 index 0000000..da20b8e Binary files /dev/null and b/figs/fig13.png differ diff --git a/slides.pdf b/slides.pdf index 810c521..1dd7eb6 100644 Binary files a/slides.pdf and b/slides.pdf differ diff --git a/theme/beamerouterthemeOUCSlide.sty b/theme/beamerouterthemeOUCSlide.sty index d7fa956..735b9ae 100644 --- a/theme/beamerouterthemeOUCSlide.sty +++ b/theme/beamerouterthemeOUCSlide.sty @@ -183,19 +183,261 @@ \setbeamertemplate{frametitle}[frame] } +%% ====================================================================== +%% 目录页:自适应方框样式 +%% ---------------------------------------------------------------------- +%% 把每一个一级标题(\section)放进一个方框里。方框的高度、以及方框内 +%% 文字的字号,都会根据一级标题的数目自动计算,并自动均分填满整页。 +%% +%% 可调参数(在导言区用 \setlength 覆盖即可): +%% \tocareamargin 目录区上下预留的高度(自动模式下 = +%% \textheight 减去页眉页脚,默认 52pt) +%% \tocareaheight 直接指定目录区高度;设为 0pt(默认)表示自动 +%% \tocboxgap 方框之间的理想间距(空间紧张时会自动压缩) +%% \tocboxgapmin 方框之间允许的最小间距 +%% \tocboxmaxheight 单个方框的最大高度 +%% \tocfontmax 方框内文字的最大字号 +%% \tocfontwarn 字号低于该值时会给出警告(提示章节太多) +%% +%% 方框样式(在导言区用 \settocstyle{...} 选择): +%% box 浅色底 + 圆角框 + 圆形序号(默认) +%% solid 实心色底 + 白色文字 +%% bar 白底 + 左侧粗色条 +%% +%% 配色主题(在导言区用 \toctheme{...} 选择): +%% red 海大红(默认),与各章节标题页的颜色一致 +%% blue 海大蓝,与目录页上「目录」二字的颜色一致 +%% ====================================================================== + +\newlength{\tocareamargin} +\setlength{\tocareamargin}{52pt} +\newlength{\tocareaheight} +\setlength{\tocareaheight}{\z@} +\newlength{\tocboxgap} +\setlength{\tocboxgap}{10pt} +\newlength{\tocboxgapmin} +\setlength{\tocboxgapmin}{2pt} +\newlength{\tocboxmaxheight} +\setlength{\tocboxmaxheight}{110pt} +\newlength{\tocboxleftpad} +\setlength{\tocboxleftpad}{2.5pt} +\newlength{\tocfontmax} +\setlength{\tocfontmax}{26pt} +\newlength{\tocfontwarn} +\setlength{\tocfontwarn}{8pt} + +\makeatletter +\newcount\ouc@toc@count +\newlength\ouc@toc@avail +\newlength\ouc@toc@gap +\newlength\ouc@toc@height +\newlength\ouc@toc@font +\newlength\ouc@toc@base +\newlength\ouc@toc@pad +\newlength\ouc@toc@padx +\newlength\ouc@toc@badgesize +\newlength\ouc@toc@tmp + +\def\ouc@toc@style{box} +\newcommand{\settocstyle}[1]{\def\ouc@toc@style{#1}} + +%% 配色主题:red = 海大红(默认,与章节标题页同色) +%% blue = 海大蓝(与目录页标题「目录」同色) +\def\ouc@toc@theme{red} +\newcommand{\toctheme}[1]{% + \def\ouc@toc@theme{#1}% + \ifcsname ouc@toc@setcolors@#1\endcsname + \csname ouc@toc@setcolors@#1\endcsname + \else + \PackageWarning{oucslide}{未知的目录页配色 “#1”, + 可选值为 red / blue,这里按 red 处理}% + \ouc@toc@setcolors@red + \fi +} +%% 红色主题:主色 oucred,浅底 oucred10 +\def\ouc@toc@setcolors@red{% + \def\ouc@toc@main{oucred}% + \def\ouc@toc@light{oucred10}% +} +%% 蓝色主题:主色 oucblue,浅底 oucblue10 +\def\ouc@toc@setcolors@blue{% + \def\ouc@toc@main{oucblue}% + \def\ouc@toc@light{oucblue10}% +} +\ouc@toc@setcolors@red + +%% 左对齐时给文字留出的左内边距 +\def\ouc@toc@indent{\dimexpr\tocboxleftpad\relax} + +\long\def\ouc@toc@gobblevi#1#2#3#4#5#6{} +\long\def\ouc@toc@gobblevii#1#2#3#4#5#6#7{} + +%% 方框内文字的字体(字号由 \ouc@toc@compute 算出) +\def\ouc@toc@fontsel{\fontsize{\ouc@toc@font}{\ouc@toc@base}\selectfont\bfseries} + +%% 圆形序号徽标(#1 = 圆圈填充色,#2 = 数字) +\def\ouc@toc@badge#1#2{% + \tikz[baseline=(ouc@badge.base)]{% + \node[circle, fill=#1, text=white, inner sep=0pt, + minimum size=\ouc@toc@badgesize, font=\bfseries] (ouc@badge) {\strut#2};% + }% +} +%% 实心样式下用的空心序号(#1 = 描边色,#2 = 数字) +\def\ouc@toc@badgeopen#1#2{% + \tikz[baseline=(ouc@badge.base)]{% + \node[circle, draw=#1, line width=0.8pt, text=#1, inner sep=0pt, + minimum size=\ouc@toc@badgesize, font=\bfseries] (ouc@badge) {\strut#2};% + }% +} + +%% ---------------------------------------------------------------- 计数 +%% 读入上一次编译生成的 .toc 文件,数出一共有多少个一级标题 +\def\ouc@toc@countsections{% + \global\ouc@toc@count\z@ + \begingroup + \makeatletter + \def\beamer@sectionintoc##1##2##3##4##5{\global\advance\ouc@toc@count\@ne}% + \let\beamer@subsectionintoc\ouc@toc@gobblevi + \let\beamer@subsubsectionintoc\ouc@toc@gobblevii + \@input{\jobname.toc}% + \makeatother + \endgroup +} + +%% ------------------------------------------------------------ 计算尺寸 +\def\ouc@toc@compute{% + %% 1. 目录区可用高度 + \ifdim\tocareaheight=\z@ + \setlength{\ouc@toc@avail}{\dimexpr\textheight-\tocareamargin\relax}% + \else + \setlength{\ouc@toc@avail}{\tocareaheight}% + \fi + \ifnum\ouc@toc@count<\@ne \global\ouc@toc@count\@ne \fi + %% 2. 间距:理想值 \tocboxgap;空间紧张时压缩,但不小于 \tocboxgapmin + \setlength{\ouc@toc@gap}{\tocboxgap}% + \setlength{\ouc@toc@tmp}{% + \dimexpr\ouc@toc@avail/(\ouc@toc@count+1)*25/100\relax}% + \ifdim\ouc@toc@gap>\ouc@toc@tmp \setlength{\ouc@toc@gap}{\ouc@toc@tmp}\fi + \ifdim\ouc@toc@gap<\tocboxgapmin \setlength{\ouc@toc@gap}{\tocboxgapmin}\fi + %% 3. 方框高度:扣掉 N+1 个间距后,把目录区高度均分给 N 个方框 + \setlength{\ouc@toc@height}{% + \dimexpr(\ouc@toc@avail-\ouc@toc@count\ouc@toc@gap-\ouc@toc@gap) + /\ouc@toc@count\relax}% + \ifdim\ouc@toc@height>\tocboxmaxheight + \setlength{\ouc@toc@height}{\tocboxmaxheight}\fi + \ifdim\ouc@toc@height<8pt \setlength{\ouc@toc@height}{8pt}\fi + %% 4. 字号:随方框高度等比缩放,同时保证文字不会撑破方框 + %% 内边距也随方框高度缩小,避免方框过矮时被 tcolorbox 撑回去 + \setlength{\ouc@toc@pad}{\dimexpr\ouc@toc@height*12/100\relax}% + \ifdim\ouc@toc@pad>2pt \setlength{\ouc@toc@pad}{2pt}\fi + \setlength{\ouc@toc@padx}{\dimexpr\ouc@toc@pad*3\relax}% + \ifdim\ouc@toc@padx<6pt \setlength{\ouc@toc@padx}{6pt}\fi + %% 序号徽标的直径:随字号缩放,下限 1.15em(免得章节多时圆圈看不清), + %% 但绝不能超过方框高度,否则会把矮方框撑破 + \setlength{\ouc@toc@badgesize}{\dimexpr\ouc@toc@font*115/100\relax}% + \ifdim\ouc@toc@badgesize<1.15em \setlength{\ouc@toc@badgesize}{1.15em}\fi + \ifdim\ouc@toc@badgesize>2.2em \setlength{\ouc@toc@badgesize}{2.2em}\fi + \setlength{\ouc@toc@tmp}{\dimexpr\ouc@toc@height-2pt\relax}% + \ifdim\ouc@toc@badgesize>\ouc@toc@tmp \setlength{\ouc@toc@badgesize}{\ouc@toc@tmp}\fi + \ifdim\ouc@toc@badgesize<4pt \setlength{\ouc@toc@badgesize}{4pt}\fi + \setlength{\ouc@toc@font}{\dimexpr\ouc@toc@height*40/100\relax}% + \setlength{\ouc@toc@tmp}{% + \dimexpr(\ouc@toc@height-2\ouc@toc@pad-2pt)*5/6\relax}% + \ifdim\ouc@toc@font>\ouc@toc@tmp \setlength{\ouc@toc@font}{\ouc@toc@tmp}\fi + \ifdim\ouc@toc@font>\tocfontmax \setlength{\ouc@toc@font}{\tocfontmax}\fi + \ifdim\ouc@toc@font<\tocfontwarn + \PackageWarning{oucslide}{目录页有 \the\ouc@toc@count\space 个一级标题, + 字号已被压缩到 \the\ouc@toc@font(低于 \the\tocfontwarn)。 + 如观感不佳,请减少 section 的数目,或调小 \string\tocareamargin}% + \fi + \setlength{\ouc@toc@base}{\dimexpr\ouc@toc@font*6/5\relax}% +} + +%% -------------------------------------------------------------- 条目 +%% #1 = 序号, #2 = 标题, #3 = 锚点页码 +\def\ouc@toc@entry#1#2#3{% + \ifcsname ouc@toc@entry@\ouc@toc@style\endcsname + \csname ouc@toc@entry@\ouc@toc@style\endcsname{#1}{#2}{#3}% + \else + \PackageWarning{oucslide}{未知的目录页样式 “\ouc@toc@style”, + 可选值为 box / solid / bar,这里按 box 处理}% + \ouc@toc@entry@box{#1}{#2}{#3}% + \fi +} + +%% 样式 box:浅色底 + 圆角框 + 圆形序号,文字左对齐(左侧留白) +\def\ouc@toc@entry@box#1#2#3{% + \begin{tcolorbox}[width=\textwidth, height=\ouc@toc@height, + valign=center, halign=left, arc=3pt, boxrule=0.8pt, + colframe=\ouc@toc@main, colback=\ouc@toc@light, coltext=\ouc@toc@main, + left=\ouc@toc@indent, right=\ouc@toc@padx, top=\ouc@toc@pad, bottom=\ouc@toc@pad, + boxsep=0pt, before skip=0pt, after skip=0pt, + fontupper=\ouc@toc@fontsel]% + \ouc@toc@badge{\ouc@toc@main}{#1}% + \hspace{0.8em}\hyperlink{Navigation#3}{#2}% + \end{tcolorbox}% +} + +%% 样式 solid:实心色底 + 白字,文字左对齐(左侧留白) +\def\ouc@toc@entry@solid#1#2#3{% + \begin{tcolorbox}[width=\textwidth, height=\ouc@toc@height, + valign=center, halign=left, arc=3pt, boxrule=0pt, + colframe=\ouc@toc@main, colback=\ouc@toc@main, coltext=white, + left=\ouc@toc@indent, right=\ouc@toc@padx, top=\ouc@toc@pad, bottom=\ouc@toc@pad, + boxsep=0pt, before skip=0pt, after skip=0pt, + fontupper=\ouc@toc@fontsel]% + \ouc@toc@badgeopen{white}{#1}% + \hspace{0.8em}\hyperlink{Navigation#3}{#2}% + \end{tcolorbox}% +} + +%% 样式 bar:白底 + 左侧粗色条 + 圆形序号,文字左对齐(左侧留白) +\def\ouc@toc@entry@bar#1#2#3{% + \begin{tcolorbox}[width=\textwidth, height=\ouc@toc@height, + valign=center, halign=left, arc=0pt, boxrule=0.5pt, + leftrule=8pt, colframe=\ouc@toc@main, colback=white, coltext=black, + left=\ouc@toc@indent, right=\ouc@toc@padx, top=\ouc@toc@pad, bottom=\ouc@toc@pad, + boxsep=0pt, before skip=0pt, after skip=0pt, + fontupper=\ouc@toc@fontsel]% + \ouc@toc@badge{\ouc@toc@main}{#1}% + \hspace{0.8em}\hyperlink{Navigation#3}{#2}% + \end{tcolorbox}% +} + +%% ---------------------------------------------------------- 目录页正文 +%% 逐条读入 .toc,把每个一级标题渲染成一个方框; +%% 方框之间、方框与页面上下边缘之间各放一个 \vfill, +%% 由 beamer 自动等分剩余空间,从而刚好铺满整页。 +\def\ouc@toc@body{% + \ouc@toc@countsections + \ouc@toc@compute + \begingroup + \makeatletter + \let\beamer@subsectionintoc\ouc@toc@gobblevi + \let\beamer@subsubsectionintoc\ouc@toc@gobblevii + \def\beamer@sectionintoc##1##2##3##4##5{% + \vfill\ouc@toc@entry{##5}{##2}{##3}% + }% + \@input{\jobname.toc}% + \makeatother + \vfill + \endgroup +} + %定义目录页生成命令 \def\makeoutline{ { \setbeamertemplate{frametitle}[outlineframe] \begin{frame} \frametitle{目录} - \tableofcontents[hideallsubsections] + \ouc@toc@body \end{frame} } %再把模板改回去 \setbeamertemplate{frametitle}[frame] } +\makeatother %定义最后一页生成命令 \def\makelast{