├── LICENSE ├── README.md ├── assets └── vi │ ├── nju-emblem-purple.svg │ ├── nju-emblem.svg │ ├── nju-name-purple.svg │ └── nju-name.svg ├── fonts ├── Arial │ └── Arial.otf ├── FangZheng │ ├── FangZhengFangSongJianTi-1.ttf │ └── FangZhengKaiTiJianTi-1.ttf └── TimesNewRoman │ ├── TimesNewRoman-Bold-Italic.ttf │ ├── TimesNewRoman-Bold.ttf │ ├── TimesNewRoman-Italic.ttf │ └── TimesNewRoman.ttf ├── imgs ├── editor.png ├── gallery.png ├── proposal.png ├── template.png └── webapp.png ├── layouts ├── appendix.typ ├── doc.typ ├── mainmatter.typ └── preface.typ ├── lib.typ ├── others ├── bachelor-proposal.typ ├── master-proposal-logo.png ├── master-proposal.typ └── style.typ ├── pages ├── acknowledgement.typ ├── bachelor-abstract-en.typ ├── bachelor-abstract.typ ├── bachelor-cover.typ ├── bachelor-decl-page.typ ├── bachelor-outline-page.typ ├── fonts-display-page.typ ├── list-of-figures.typ ├── list-of-tables.typ ├── master-abstract-en.typ ├── master-abstract.typ ├── master-cover.typ ├── master-decl-page.typ └── notation.typ ├── template ├── images │ └── nju-emblem.svg ├── ref.bib └── thesis.typ ├── thumbnail.png ├── typst.toml └── utils ├── bilingual-bibliography.typ ├── custom-cuti.typ ├── custom-heading.typ ├── custom-numbering.typ ├── custom-tablex.typ ├── datetime-display.typ ├── double-underline.typ ├── hline.typ ├── invisible-heading.typ ├── justify-text.typ ├── style.typ └── unpairs.typ /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 OrangeX4 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 南京大学学位论文 modern-nju-thesis 2 | 3 | 南京大学毕业论文(设计)的 Typst 模板,能够简洁、快速、持续生成 PDF 格式的毕业论文。[Typst Universe](https://typst.app/universe/package/modern-nju-thesis) 4 | 5 | Typst 非官方中文交流群:793548390 6 | 7 | 南京大学 Typst 交流群:943622984 8 | 9 | ![](imgs/editor.png) 10 | 11 | ## 劣势 12 | 13 | - Typst 是一门新生的排版标记语言,还做不到像 Word 或 LaTeX 一样成熟稳定。 14 | - 该模板并非官方模板,而是民间模板,**存在不被认可的风险**。 15 | 16 | 17 | ## 优势 18 | 19 | Typst 是可用于出版的可编程标记语言,拥有变量、函数与包管理等现代编程语言的特性,注重于科学写作 (science writing),定位与 LaTeX 相似。可以阅读我的 [一篇知乎文章](https://zhuanlan.zhihu.com/p/669097092) 进一步了解 Typst 的优势。 20 | 21 | - **语法简洁**:上手难度跟 Markdown 相当,文本源码阅读性高,不会像 LaTeX 一样充斥着反斜杠与花括号。 22 | - **编译速度快**:Typst 使用 Rust 语言编写,目标运行平台是WASM,即浏览器本地离线运行;也可以编译成命令行工具,采用一种 **增量编译** 算法和一种有约束的版面缓存方案,**文档长度基本不会影响编译速度,且编译速度与常见 Markdown 渲染引擎渲染速度相当**。 23 | - **环境搭建简单**:不需要像 LaTeX 一样折腾几个 G 的开发环境,原生支持中日韩等非拉丁语言,无论是官方 Web App 在线编辑,还是使用 VS Code 安装插件本地开发,都是 **即开即用**。 24 | - **现代编程语言**:Typst 是可用于出版的可编程标记语言,拥有 **变量、函数、包管理与错误检查** 等现代编程语言的特性,同时也提供了 **闭包** 等特性,便于进行 **函数式编程**。以及包括了 `[标记模式]`、`{脚本模式}` 与 `$数学模式$` 等多种模式的作用域,并且它们可以不限深度地、交互地嵌套。并且通过 **包管理**,你不再需要像 TexLive 一样在本地安装一大堆并不必要的宏包,而是 **按需自动从云端下载**。 25 | 26 | 可以参考我参与搭建和翻译的 [Typst 中文文档网站](https://typst-doc-cn.github.io/docs/) 迅速入门。 27 | 28 | 29 | ## 使用 30 | 31 | 快速浏览效果: 查看 [thesis.pdf](https://github.com/nju-lug/modern-nju-thesis/releases/latest/download/thesis.pdf),样例论文源码:查看 [thesis.typ](https://github.com/nju-lug/modern-nju-thesis/blob/main/template/thesis.typ) 32 | 33 | **你只需要修改 `thesis.typ` 文件即可,基本可以满足你的所有需求。** 34 | 35 | 如果你认为不能满足你的需求,可以先查阅后面的 [Q&A](#Q%26A) 部分。 36 | 37 | 模板已经上传到了 Typst Universe,使用起来十分简单,理论上只需要通过 38 | 39 | ```typst 40 | #import "@preview/modern-nju-thesis:0.4.0": documentclass 41 | ``` 42 | 43 | 导入即可。 44 | 45 | ### 在线编辑 46 | 47 | Typst 提供了官方的 Web App,支持像 Overleaf 一样在线编辑,这是一个 [例子](https://typst.app/project/rgiwHIjdPOnXr9HJb8H0oa)。 48 | 49 | 实际上,我们只需要在 [Web App](https://typst.app/?template=modern-nju-thesis&version=0.4.0) 中的 `Start from template` 里选择 `modern-nju-thesis`,即可在线创建模板并使用。 50 | 51 | ![](imgs/template.png) 52 | 53 | ![](imgs/webapp.png) 54 | 55 | **但是 Web App 并没有安装本地 Windows 或 MacOS 所拥有的字体,所以字体上可能存在差异,所以推荐本地编辑!** 56 | 57 | **你需要手动上传 fonts 目录下的字体文件到项目中,否则会导致字体显示错误!** 58 | 59 | PS: 虽然与 Overleaf 看起来相似,但是它们底层原理并不相同。Overleaf 是在后台服务器运行了一个 LaTeX 编译器,本质上是计算密集型的服务;而 Typst 只需要在浏览器端使用 WASM 技术执行,本质上是 IO 密集型的服务,所以对服务器压力很小(只需要负责文件的云存储与协作同步功能)。 60 | 61 | 62 | ### VS Code 本地编辑(推荐) 63 | 64 | 1. 在 VS Code 中安装 [Tinymist Typst](https://marketplace.visualstudio.com/items?itemName=myriad-dreamin.tinymist)。 65 | 2. 按下 `Ctrl + Shift + P` 打开命令界面,输入 `Typst: Show available Typst templates (gallery) for picking up a template` 打开 Tinymist 提供的 Template Gallery,然后从里面找到 `modern-nju-thesis`,点击 `❤` 按钮进行收藏,以及点击 `+` 号,就可以创建对应的论文模板了。 66 | 3. 最后用 VS Code 打开生成的目录,打开 `thesis.typ` 文件,并按下 `Ctrl + K V` 进行实时编辑和预览。 67 | 68 | ![](imgs/gallery.png) 69 | 70 | ### 特性 / 路线图 71 | 72 | - **说明文档** 73 | - [ ] 编写更详细的说明文档,后续考虑使用 [tidy](https://github.com/typst/packages/tree/main/packages/preview/tidy/0.1.0) 编写,你现在可以先参考 [NJUThesis](https://mirror-hk.koddos.net/CTAN/macros/unicodetex/latex/njuthesis/njuthesis.pdf) 的文档,参数大体保持一致,或者直接查阅对应源码函数的参数 74 | - **类型检查** 75 | - [ ] 应该对所有函数入参进行类型检查,及时报错 76 | - **全局配置** 77 | - [x] 类似 LaTeX 中的 `documentclass` 的全局信息配置 78 | - [x] **盲审模式**,将个人信息替换成小黑条,并且隐藏致谢页面,论文提交阶段使用 79 | - [x] **双面模式**,会加入空白页,便于打印 80 | - [x] **自定义字体配置**,可以配置「宋体」、「黑体」与「楷体」等字体对应的具体字体 81 | - [x] **数学字体配置**:模板不提供配置,用户可以自己使用 `#show math.equation: set text(font: "Fira Math")` 更改 82 | - **模板** 83 | - [x] 本科生模板 84 | - [x] 字体测试页 85 | - [x] 封面 86 | - [x] 声明页 87 | - [x] 中文摘要 88 | - [x] 英文摘要 89 | - [x] 目录页 90 | - [x] 插图目录 91 | - [x] 表格目录 92 | - [x] 符号表 93 | - [x] 致谢 94 | - [x] 研究生模板 95 | - [x] 封面 96 | - [x] 声明页 97 | - [x] 摘要 98 | - [x] 页眉 99 | - [ ] 国家图书馆封面 100 | - [ ] 出版授权书 101 | - [ ] 博士后模板 102 | - **编号** 103 | - [x] 前言使用罗马数字编号 104 | - [x] 附录使用罗马数字编号 105 | - [x] 表格使用 `1.1` 格式进行编号 106 | - [x] 数学公式使用 `(1.1)` 格式进行编号 107 | - **环境** 108 | - [ ] 定理环境(这个也可以自己使用第三方包配置) 109 | - **其他文件** 110 | - [x] 本科生开题报告 111 | - [x] 研究生开题报告 112 | 113 | 114 | ## 其他文件 115 | 116 | 还实现了本科生和研究生的开题报告,只需要预览和编辑 `others` 目录下的文件即可。 117 | 118 | ![开题报告](./imgs/proposal.png) 119 | 120 | 121 | ## Q&A 122 | 123 | ### 我不会 LaTeX,可以用这个模板写论文吗? 124 | 125 | 可以。 126 | 127 | 如果你不关注模板的具体实现原理,你可以用 Markdown Like 的语法进行编写,只需要按照模板的结构编写即可。 128 | 129 | 130 | ### 我不会编程,可以用这个模板写论文吗? 131 | 132 | 同样可以。 133 | 134 | 如果仅仅是当成是入门一款类似于 Markdown 的语言,相信使用该模板的体验会比使用 Word 编写更好。 135 | 136 | 137 | ### 为什么我的字体没有显示出来,而是一个个「豆腐块」? 138 | 139 | 这是因为本地没有对应的字体,**这种情况经常发生在 MacOS 的「楷体」显示上**。 140 | 141 | 你应该安装本目录下的 `fonts` 里的所有字体,里面包含了可以免费商用的「方正楷体」和「方正仿宋」,然后再重新渲染测试即可。 142 | 143 | 你可以使用 `#fonts-display-page()` 显示一个字体渲染测试页面,查看对应的字体是否显示成功。 144 | 145 | 如果还是不能成功,你可以按照模板里的说明自行配置字体,例如 146 | 147 | ```typst 148 | #let (...) = documentclass( 149 | fonts: (楷体: ((name: "Times New Roman", covers: "latin-in-cjk"), "FZKai-Z03S")), 150 | ) 151 | ``` 152 | 153 | 先是填写英文字体,然后再填写你需要的「楷体」中文字体。 154 | 155 | **字体名称可以通过 `typst fonts` 命令查询。** 156 | 157 | 如果找不到你所需要的字体,可能是因为 **该字体变体(Variants)数量过少**,导致 Typst 无法识别到该中文字体。 158 | 159 | 160 | ### 为什么 Typst 有很多关于字体的警告? 161 | 162 | 你会发现 Typst 有许多关于字体的警告,这是因为 modern-nju-thesis 加入了很多不必要的 fallback 字体,你可以自定义字体消除警告,先英文字体后中文字体,应传入「宋体」、「黑体」、「楷体」、「仿宋」、「等宽」。 163 | 164 | ```typst 165 | #let (...) = documentclass( 166 | fonts: (楷体: ((name: "Times New Roman", covers: "latin-in-cjk"), "FZKai-Z03S")), 167 | ) 168 | ``` 169 | 170 | 171 | ### 学习 Typst 需要多久? 172 | 173 | 一般而言,仅仅进行简单的编写,不关注布局的话,你可以打开模板就开始写了。 174 | 175 | 如果你想进一步学习 Typst 的语法,例如如何排篇布局,如何设置页脚页眉等,一般只需要几个小时就能学会。 176 | 177 | 如果你还想学习 Typst 的「[参考](https://typst-doc-cn.github.io/docs/reference/foundations/)」部分,进而能够编写自己的模板,一般而言需要几天的时间阅读文档,以及他人编写的模板代码。 178 | 179 | 如果你有 Python 或 JavaScript 等脚本语言的编写经验,了解过函数式编程、宏、样式、组件化开发等概念,入门速度会快很多。 180 | 181 | 182 | ### 我有编写 LaTeX 的经验,如何快速入门? 183 | 184 | 可以参考 [小蓝书](https://typst-doc-cn.github.io/tutorial/) 和 [FAQ](https://typst-doc-cn.github.io/guide/)。 185 | 186 | 一些常见问题: 187 | 188 | - [如何在段落内加入行间数学公式以避免首行缩进?](https://typst-doc-cn.github.io/guide/FAQ/block-equation-in-paragraph.html) 189 | 190 | 191 | ### 目前 Typst 有哪些第三方包和模板? 192 | 193 | 可以查看 [Typst Universe](https://typst.app/universe)。 194 | 195 | 我个人推荐的包: 196 | 197 | - 基础绘图:[cetz](https://typst.app/universe/package/cetz) 198 | - 绘制带有节点和箭头的图表,如流程图等:[fletcher](https://typst.app/universe/package/fletcher) 199 | - 定理环境:[theorion](https://typst.app/universe/package/theorion) 200 | - 伪代码:[lovelace](https://typst.app/universe/package/lovelace) 201 | - 带行号的代码显示包:[zebraw](https://typst.app/universe/package/zebraw) 202 | - 简洁的 Numbering 包:[numbly](https://typst.app/universe/package/numbly) 203 | - 幻灯片和演示文档:[touying](https://typst.app/universe/package/touying) 204 | - 相对定位布局包:[pinit](https://typst.app/universe/package/pinit) 205 | - 数学单位包:[unify](https://typst.app/universe/package/unify) 206 | - 数字格式化包:[zero](https://typst.app/universe/package/zero) 207 | - 写 LaTeX 数学公式:[mitex](https://typst.app/universe/package/mitex) 208 | - 写原生 Markdown:[cmarker](https://typst.app/universe/package/cmarker) 209 | - 写 Markdown-like checklist:[cheq](https://typst.app/universe/package/cheq) 210 | - 写 Markdown-like 表格:[tablem](https://typst.app/universe/package/tablem) 211 | 212 | 213 | ### 为什么只有一个 thesis.typ 文件,没有按章节分多个文件? 214 | 215 | 因为 Typst **语法足够简洁**、**编译速度足够快**、并且 **拥有光标点击处双向跳转功能**。 216 | 217 | 语法简洁的好处是,即使把所有内容都写在同一个文件,你也可以很简单地分辨出各个部分的内容。 218 | 219 | 编译速度足够快的好处是,你不再需要像 LaTeX 一样,将内容分散在几个文件,并通过注释的方式提高编译速度。 220 | 221 | 光标点击处双向跳转功能,使得你可以直接拖动预览窗口到你想要的位置,然后用鼠标点击即可到达对应源码所在位置。 222 | 223 | 还有一个好处是,单个源文件便于同步和分享。 224 | 225 | 即使你还是想要分成几个章节,也是可以的,Typst 支持你使用 `#import` 和 `#include` 语法将其他文件的内容导入或置入。你可以新建文件夹 `chapters`,然后将各个章节的源文件放进去,然后通过 `#include` 置入 `thesis.typ` 里。 226 | 227 | 228 | ### 我如何更改页面上的样式?具体的语法是怎么样的? 229 | 230 | 理论上你并不需要更改 `nju-thesis` 目录下的任何文件,无论是样式还是其他的配置,你都可以在 `thesis.typ` 文件内修改函数参数实现更改。具体的更改方式可以阅读 `nju-thesis` 目录下的文件的函数参数。 231 | 232 | 例如,想要更改页面边距为 `50pt`,只需要将 233 | 234 | ```typst 235 | #show: doc 236 | ``` 237 | 238 | 改为 239 | 240 | ```typst 241 | #show: doc.with(margin: (x: 50pt)) 242 | ``` 243 | 244 | 即可。 245 | 246 | 后续我也会编写一个更详细的文档,可能会考虑使用 [tidy](https://github.com/typst/packages/tree/main/packages/preview/tidy/0.1.0) 来编写。 247 | 248 | 如果你阅读了那些函数的参数,仍然不知道如何修改得到你需要的样式,欢迎提出 Issue,只要描述清楚问题即可。 249 | 250 | 或者也欢迎加群讨论:943622984 251 | 252 | 253 | ### 该模板和其他现存 Typst 中文论文模板的区别? 254 | 255 | 其他现存的 Typst 中文论文模板大多都是在 2023 年 7 月份之前(Typst Verison 0.6 之前)开发的,当时 Typst 还不不够成熟,甚至连 **包管理** 功能都还没有,因此当时的 Typst 中文论文模板的开发者基本都是自己从头写了一遍需要的功能/函数,因此造成了 **代码耦合度高**、**意大利面条式代码**、**重复造轮子** 与 **难以自定义样式** 等问题。 256 | 257 | 该模板是在 2023 年 10 ~ 11 月份(Typst Verison 0.9 时)开发的,此时 Typst 语法基本稳定,并且提供了 **包管理** 功能,因此能够减少很多不必要的代码。 258 | 259 | PS:2025 年 2 月进行了 Typst 0.13 的迁移更新。 260 | 261 | 并且我对模板的文件架构进行了解耦,主要分为了 `utils`、`pages` 和 `layouts` 三个目录,这三个目录可以看后文的开发者指南,并且使用 **闭包** 特性实现了类似不可变全局变量的全局配置能力,即模板中的 `documentclass` 函数类。 262 | 263 | 264 | ### 我不是南京大学本科生,如何迁移该模板? 265 | 266 | 我在开发的过程中已经对模板的各个模板进行了解耦,理论上你只需要在 `pages` 目录中加入你需要的页面,然后更改少许、或者不需要更改其他目录的代码。 267 | 268 | 具体目录职责划分可以看下面的开发者指南。 269 | 270 | 271 | ## 开发者指南 272 | 273 | ### template 目录 274 | 275 | - `thesis.typ` 文件: 你的论文源文件,可以随意更改这个文件的名字,甚至你可以将这个文件在同级目录下复制多份,维持多个版本。 276 | - `ref.bib` 文件: 用于放置参考文献。 277 | - `images` 目录: 用于放置图片。 278 | 279 | 280 | ### 内部目录 281 | 282 | - `utils` 目录: 包含了模板使用到的各种自定义辅助函数,存放没有外部依赖,且 **不会渲染出页面的函数**。 283 | - `pages` 目录: 包含了模板用到的各个 **独立页面**,例如封面页、声明页、摘要等,即 **会渲染出不影响其他页面的独立页面的函数**。 284 | - `layouts` 目录: 布局目录,存放着用于排篇布局的、应用于 `show` 指令的、**横跨多个页面的函数**,例如为了给页脚进行罗马数字编码的前言 `preface` 函数。 285 | - 主要分成了 `doc` 文稿、`preface` 前言、`mainmatter` 正文与 `appendix` 附录/后记。 286 | - `lib.typ`: 287 | - **职责一**: 作为一个统一的对外接口,暴露出内部的 utils 函数。 288 | - **职责二**: 使用 **函数闭包** 特性,通过 `documentclass` 函数类进行全局信息配置,然后暴露出拥有了全局配置的、具体的 `layouts` 和 `pages` 内部函数。 289 | 290 | 291 | ## 参与贡献 292 | 293 | - 在 Issues 中提出你的想法,如果是新特性,可以加入路线图! 294 | - 实现路线图中仍未实现的部分,然后欢迎提出你的 PR。 295 | - 同样欢迎 **将这个模板迁移至你的学校论文模板**,大家一起搭建更好的 Typst 社区和生态吧。 296 | 297 | 298 | ## 致谢 299 | 300 | - 感谢 [@atxy-blip](https://github.com/atxy-blip) 开发的 [NJUThesis](https://github.com/nju-lug/NJUThesis) LaTeX 模板,文档十分详细,本模板大体结构都是参考 NJUThesis 的文档开发的。 301 | - 感谢 [@csimide](https://github.com/csimide) 帮忙补充的 [bilingual-bibliography](https://github.com/nju-lug/modern-nju-thesis/issues/3)。 302 | - 感谢 [HUST-typst-template](https://github.com/werifu/HUST-typst-template) 与 [sysu-thesis-typst](https://github.com/howardlau1999/sysu-thesis-typst) 等 Typst 中文论文模板。 303 | 304 | 305 | ## License 306 | 307 | This project is licensed under the MIT License. 308 | -------------------------------------------------------------------------------- /assets/vi/nju-emblem.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /assets/vi/nju-name-purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/vi/nju-name.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /fonts/Arial/Arial.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/fonts/Arial/Arial.otf -------------------------------------------------------------------------------- /fonts/FangZheng/FangZhengFangSongJianTi-1.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/fonts/FangZheng/FangZhengFangSongJianTi-1.ttf -------------------------------------------------------------------------------- /fonts/FangZheng/FangZhengKaiTiJianTi-1.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/fonts/FangZheng/FangZhengKaiTiJianTi-1.ttf -------------------------------------------------------------------------------- /fonts/TimesNewRoman/TimesNewRoman-Bold-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/fonts/TimesNewRoman/TimesNewRoman-Bold-Italic.ttf -------------------------------------------------------------------------------- /fonts/TimesNewRoman/TimesNewRoman-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/fonts/TimesNewRoman/TimesNewRoman-Bold.ttf -------------------------------------------------------------------------------- /fonts/TimesNewRoman/TimesNewRoman-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/fonts/TimesNewRoman/TimesNewRoman-Italic.ttf -------------------------------------------------------------------------------- /fonts/TimesNewRoman/TimesNewRoman.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/fonts/TimesNewRoman/TimesNewRoman.ttf -------------------------------------------------------------------------------- /imgs/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/imgs/editor.png -------------------------------------------------------------------------------- /imgs/gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/imgs/gallery.png -------------------------------------------------------------------------------- /imgs/proposal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/imgs/proposal.png -------------------------------------------------------------------------------- /imgs/template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/imgs/template.png -------------------------------------------------------------------------------- /imgs/webapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/imgs/webapp.png -------------------------------------------------------------------------------- /layouts/appendix.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/i-figured:0.2.4" 2 | #import "../utils/custom-numbering.typ": custom-numbering 3 | 4 | // 后记,重置 heading 计数器 5 | #let appendix( 6 | numbering: custom-numbering.with(first-level: "", depth: 4, "1.1 "), 7 | // figure 计数 8 | show-figure: i-figured.show-figure.with(numbering: "1.1"), 9 | // equation 计数 10 | show-equation: i-figured.show-equation.with(numbering: "(1.1)"), 11 | // 重置计数 12 | reset-counter: false, 13 | it, 14 | ) = { 15 | set heading(numbering: numbering) 16 | if reset-counter { 17 | counter(heading).update(0) 18 | } 19 | // 设置 figure 的编号 20 | show figure: show-figure 21 | // 设置 equation 的编号 22 | show math.equation.where(block: true): show-equation 23 | it 24 | } -------------------------------------------------------------------------------- /layouts/doc.typ: -------------------------------------------------------------------------------- 1 | // 文稿设置,可以进行一些像页面边距这类的全局设置 2 | #let doc( 3 | // documentclass 传入参数 4 | info: (:), 5 | // 其他参数 6 | fallback: false, // 字体缺失时使用 fallback,不显示豆腐块 7 | lang: "zh", 8 | margin: (x: 89pt), 9 | it, 10 | ) = { 11 | // 1. 默认参数 12 | info = ( 13 | title: ("基于 Typst 的", "南京大学学位论文"), 14 | author: "张三", 15 | ) + info 16 | 17 | // 2. 对参数进行处理 18 | // 2.1 如果是字符串,则使用换行符将标题分隔为列表 19 | if type(info.title) == str { 20 | info.title = info.title.split("\n") 21 | } 22 | 23 | // 3. 基本的样式设置 24 | set text(fallback: fallback, lang: lang) 25 | set page(margin: margin) 26 | 27 | // 4. PDF 元信息 28 | set document( 29 | title: (("",)+ info.title).sum(), 30 | author: info.author, 31 | ) 32 | 33 | it 34 | } -------------------------------------------------------------------------------- /layouts/mainmatter.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/i-figured:0.2.4" 2 | #import "../utils/style.typ": 字号, 字体 3 | #import "../utils/custom-numbering.typ": custom-numbering 4 | #import "../utils/custom-heading.typ": heading-display, active-heading, current-heading 5 | #import "../utils/unpairs.typ": unpairs 6 | 7 | #let mainmatter( 8 | // documentclass 传入参数 9 | twoside: false, 10 | fonts: (:), 11 | // 其他参数 12 | leading: 1.5 * 15.6pt - 0.7em, 13 | spacing: 1.5 * 15.6pt - 0.7em, 14 | justify: true, 15 | first-line-indent: (amount: 2em, all: true), 16 | numbering: custom-numbering.with(first-level: "第一章 ", depth: 4, "1.1 "), 17 | // 正文字体与字号参数 18 | text-args: auto, 19 | // 标题字体与字号 20 | heading-font: auto, 21 | heading-size: (字号.四号,), 22 | heading-weight: ("regular",), 23 | heading-above: (2 * 15.6pt - 0.7em, 2 * 15.6pt - 0.7em), 24 | heading-below: (2 * 15.6pt - 0.7em, 1.5 * 15.6pt - 0.7em), 25 | heading-pagebreak: (true, false), 26 | heading-align: (center, auto), 27 | // 页眉 28 | header-render: auto, 29 | header-vspace: 0em, 30 | display-header: false, 31 | skip-on-first-level: true, 32 | stroke-width: 0.5pt, 33 | reset-footnote: true, 34 | // caption 的 separator 35 | separator: " ", 36 | // caption 样式 37 | caption-style: strong, 38 | caption-size: 字号.五号, 39 | // figure 计数 40 | show-figure: i-figured.show-figure, 41 | // equation 计数 42 | show-equation: i-figured.show-equation, 43 | ..args, 44 | it, 45 | ) = { 46 | // 0. 标志前言结束 47 | set page(numbering: "1") 48 | 49 | // 1. 默认参数 50 | fonts = 字体 + fonts 51 | if text-args == auto { 52 | text-args = (font: fonts.宋体, size: 字号.小四) 53 | } 54 | // 1.1 字体与字号 55 | if heading-font == auto { 56 | heading-font = (fonts.黑体,) 57 | } 58 | // 1.2 处理 heading- 开头的其他参数 59 | let heading-text-args-lists = args.named().pairs() 60 | .filter((pair) => pair.at(0).starts-with("heading-")) 61 | .map((pair) => (pair.at(0).slice("heading-".len()), pair.at(1))) 62 | 63 | // 2. 辅助函数 64 | let array-at(arr, pos) = { 65 | arr.at(calc.min(pos, arr.len()) - 1) 66 | } 67 | 68 | // 3. 设置基本样式 69 | // 3.1 文本和段落样式 70 | set text(..text-args) 71 | set par( 72 | leading: leading, 73 | justify: justify, 74 | first-line-indent: first-line-indent, 75 | spacing: spacing, 76 | ) 77 | show raw: set text(font: fonts.等宽) 78 | // 3.2 脚注样式 79 | show footnote.entry: set text(font: fonts.宋体, size: 字号.五号) 80 | // 3.3 设置 figure 的编号 81 | show heading: i-figured.reset-counters 82 | show figure: show-figure 83 | // 3.4 设置 equation 的编号和假段落首行缩进 84 | show math.equation.where(block: true): show-equation 85 | // 3.5 表格表头置顶 + 不用冒号用空格分割 + 样式 86 | show figure.where( 87 | kind: table 88 | ): set figure.caption(position: top) 89 | set figure.caption(separator: separator) 90 | show figure.caption: caption-style 91 | show figure.caption: set text(font: fonts.宋体, size: 字号.五号) 92 | // 3.6 优化列表显示 93 | // 术语列表 terms 不应该缩进 94 | show terms: set par(first-line-indent: 0pt) 95 | 96 | // 4. 处理标题 97 | // 4.1 设置标题的 Numbering 98 | set heading(numbering: numbering) 99 | // 4.2 设置字体字号并加入假段落模拟首行缩进 100 | show heading: it => { 101 | set text( 102 | font: array-at(heading-font, it.level), 103 | size: array-at(heading-size, it.level), 104 | weight: array-at(heading-weight, it.level), 105 | ..unpairs(heading-text-args-lists 106 | .map((pair) => (pair.at(0), array-at(pair.at(1), it.level)))) 107 | ) 108 | set block( 109 | above: array-at(heading-above, it.level), 110 | below: array-at(heading-below, it.level), 111 | ) 112 | it 113 | } 114 | // 4.3 标题居中与自动换页 115 | show heading: it => { 116 | if array-at(heading-pagebreak, it.level) { 117 | // 如果打上了 no-auto-pagebreak 标签,则不自动换页 118 | if "label" not in it.fields() or str(it.label) != "no-auto-pagebreak" { 119 | pagebreak(weak: true) 120 | } 121 | } 122 | if array-at(heading-align, it.level) != auto { 123 | set align(array-at(heading-align, it.level)) 124 | it 125 | } else { 126 | it 127 | } 128 | } 129 | 130 | // 5. 处理页眉 131 | set page(..(if display-header { 132 | ( 133 | header: context { 134 | // 重置 footnote 计数器 135 | if reset-footnote { 136 | counter(footnote).update(0) 137 | } 138 | let loc = here() 139 | // 5.1 获取当前页面的一级标题 140 | let cur-heading = current-heading(level: 1) 141 | // 5.2 如果当前页面没有一级标题,则渲染页眉 142 | if not skip-on-first-level or cur-heading == none { 143 | if header-render == auto { 144 | // 一级标题和二级标题 145 | let first-level-heading = if not twoside or calc.rem(loc.page(), 2) == 0 { heading-display(active-heading(level: 1, loc)) } else { "" } 146 | let second-level-heading = if not twoside or calc.rem(loc.page(), 2) == 2 { heading-display(active-heading(level: 2, prev: false, loc)) } else { "" } 147 | set text(font: fonts.楷体, size: 字号.五号) 148 | stack( 149 | first-level-heading + h(1fr) + second-level-heading, 150 | v(0.25em), 151 | if first-level-heading != "" or second-level-heading != "" { line(length: 100%, stroke: stroke-width + black) }, 152 | ) 153 | } else { 154 | header-render(loc) 155 | } 156 | v(header-vspace) 157 | } 158 | } 159 | ) 160 | } else { 161 | ( 162 | header: { 163 | // 重置 footnote 计数器 164 | if reset-footnote { 165 | counter(footnote).update(0) 166 | } 167 | } 168 | ) 169 | })) 170 | context { 171 | if calc.even(here().page()){ 172 | set page(numbering: "I",header: none) 173 | // counter(page).update(1) 174 | pagebreak() + " " 175 | } 176 | } 177 | counter(page).update(1) 178 | 179 | it 180 | } -------------------------------------------------------------------------------- /layouts/preface.typ: -------------------------------------------------------------------------------- 1 | // 前言,重置页面计数器 2 | #let preface( 3 | // documentclass 传入的参数 4 | twoside: false, 5 | ..args, 6 | it, 7 | ) = { 8 | // 分页 9 | if twoside { 10 | pagebreak() + " " 11 | } 12 | counter(page).update(0) 13 | set page(numbering: "I") 14 | it 15 | } -------------------------------------------------------------------------------- /lib.typ: -------------------------------------------------------------------------------- 1 | // 南京大学学位论文模板 modern-nju-thesis 2 | // Author: https://github.com/OrangeX4 3 | // Repo: https://github.com/nju-lug/modern-nju-thesis 4 | // 在线模板可能不会更新得很及时,如果需要最新版本,请关注 Repo 5 | 6 | #import "layouts/doc.typ": doc 7 | #import "layouts/preface.typ": preface 8 | #import "layouts/mainmatter.typ": mainmatter 9 | #import "layouts/appendix.typ": appendix 10 | #import "pages/fonts-display-page.typ": fonts-display-page 11 | #import "pages/bachelor-cover.typ": bachelor-cover 12 | #import "pages/master-cover.typ": master-cover 13 | #import "pages/bachelor-decl-page.typ": bachelor-decl-page 14 | #import "pages/master-decl-page.typ": master-decl-page 15 | #import "pages/bachelor-abstract.typ": bachelor-abstract 16 | #import "pages/master-abstract.typ": master-abstract 17 | #import "pages/bachelor-abstract-en.typ": bachelor-abstract-en 18 | #import "pages/master-abstract-en.typ": master-abstract-en 19 | #import "pages/bachelor-outline-page.typ": bachelor-outline-page 20 | #import "pages/list-of-figures.typ": list-of-figures 21 | #import "pages/list-of-tables.typ": list-of-tables 22 | #import "pages/notation.typ": notation 23 | #import "pages/acknowledgement.typ": acknowledgement 24 | #import "utils/custom-cuti.typ": * 25 | #import "utils/bilingual-bibliography.typ": bilingual-bibliography 26 | #import "utils/custom-numbering.typ": custom-numbering 27 | #import "utils/custom-heading.typ": heading-display, active-heading, current-heading 28 | #import "@preview/i-figured:0.2.4": show-figure, show-equation 29 | #import "utils/style.typ": 字体, 字号 30 | 31 | #let indent = h(2em) 32 | 33 | // 使用函数闭包特性,通过 `documentclass` 函数类进行全局信息配置,然后暴露出拥有了全局配置的、具体的 `layouts` 和 `templates` 内部函数。 34 | #let documentclass( 35 | doctype: "bachelor", // "bachelor" | "master" | "doctor" | "postdoc",文档类型,默认为本科生 bachelor 36 | degree: "academic", // "academic" | "professional",学位类型,默认为学术型 academic 37 | nl-cover: false, // TODO: 是否使用国家图书馆封面,默认关闭 38 | twoside: false, // 双面模式,会加入空白页,便于打印 39 | anonymous: false, // 盲审模式 40 | bibliography: none, // 原来的参考文献函数 41 | fonts: (:), // 字体,应传入「宋体」、「黑体」、「楷体」、「仿宋」、「等宽」 42 | info: (:), 43 | ) = { 44 | // 默认参数 45 | fonts = 字体 + fonts 46 | info = ( 47 | title: ("基于 Typst 的", "南京大学学位论文"), 48 | title-en: "NJU Thesis Template for Typst", 49 | grade: "20XX", 50 | student-id: "1234567890", 51 | author: "张三", 52 | author-en: "Zhang San", 53 | department: "某学院", 54 | department-en: "XX Department", 55 | major: "某专业", 56 | major-en: "XX Major", 57 | field: "某方向", 58 | field-en: "XX Field", 59 | supervisor: ("李四", "教授"), 60 | supervisor-en: "Professor Li Si", 61 | supervisor-ii: (), 62 | supervisor-ii-en: "", 63 | submit-date: datetime.today(), 64 | // 以下为研究生项 65 | defend-date: datetime.today(), 66 | confer-date: datetime.today(), 67 | bottom-date: datetime.today(), 68 | chairman: "某某某 教授", 69 | reviewer: ("某某某 教授", "某某某 教授"), 70 | clc: "O643.12", 71 | udc: "544.4", 72 | secret-level: "公开", 73 | supervisor-contact: "南京大学 江苏省南京市栖霞区仙林大道163号", 74 | email: "xyz@smail.nju.edu.cn", 75 | school-code: "10284", 76 | degree: auto, 77 | degree-en: auto, 78 | ) + info 79 | 80 | return ( 81 | // 将传入参数再导出 82 | doctype: doctype, 83 | degree: degree, 84 | nl-cover: nl-cover, 85 | twoside: twoside, 86 | anonymous: anonymous, 87 | fonts: fonts, 88 | info: info, 89 | // 页面布局 90 | doc: (..args) => { 91 | doc( 92 | ..args, 93 | info: info + args.named().at("info", default: (:)), 94 | ) 95 | }, 96 | preface: (..args) => { 97 | preface( 98 | twoside: twoside, 99 | ..args, 100 | ) 101 | }, 102 | mainmatter: (..args) => { 103 | if doctype == "master" or doctype == "doctor" { 104 | mainmatter( 105 | twoside: twoside, 106 | display-header: true, 107 | ..args, 108 | fonts: fonts + args.named().at("fonts", default: (:)), 109 | ) 110 | } else { 111 | mainmatter( 112 | twoside: twoside, 113 | ..args, 114 | fonts: fonts + args.named().at("fonts", default: (:)), 115 | ) 116 | } 117 | }, 118 | appendix: (..args) => { 119 | appendix( 120 | ..args, 121 | ) 122 | }, 123 | 124 | // 字体展示页 125 | fonts-display-page: (..args) => { 126 | fonts-display-page( 127 | twoside: twoside, 128 | ..args, 129 | fonts: fonts + args.named().at("fonts", default: (:)), 130 | ) 131 | }, 132 | 133 | // 封面页,通过 type 分发到不同函数 134 | cover: (..args) => { 135 | if doctype == "master" or doctype == "doctor" { 136 | master-cover( 137 | doctype: doctype, 138 | degree: degree, 139 | nl-cover: nl-cover, 140 | anonymous: anonymous, 141 | twoside: twoside, 142 | ..args, 143 | fonts: fonts + args.named().at("fonts", default: (:)), 144 | info: info + args.named().at("info", default: (:)), 145 | ) 146 | } else if doctype == "postdoc" { 147 | panic("postdoc has not yet been implemented.") 148 | } else { 149 | bachelor-cover( 150 | anonymous: anonymous, 151 | twoside: twoside, 152 | ..args, 153 | fonts: fonts + args.named().at("fonts", default: (:)), 154 | info: info + args.named().at("info", default: (:)), 155 | ) 156 | } 157 | }, 158 | 159 | // 声明页,通过 type 分发到不同函数 160 | decl-page: (..args) => { 161 | if doctype == "master" or doctype == "doctor" { 162 | master-decl-page( 163 | anonymous: anonymous, 164 | twoside: twoside, 165 | ..args, 166 | fonts: fonts + args.named().at("fonts", default: (:)), 167 | ) 168 | } else if doctype == "postdoc" { 169 | panic("postdoc has not yet been implemented.") 170 | } else { 171 | bachelor-decl-page( 172 | anonymous: anonymous, 173 | twoside: twoside, 174 | ..args, 175 | fonts: fonts + args.named().at("fonts", default: (:)), 176 | info: info + args.named().at("info", default: (:)), 177 | ) 178 | } 179 | }, 180 | 181 | // 中文摘要页,通过 type 分发到不同函数 182 | abstract: (..args) => { 183 | if doctype == "master" or doctype == "doctor" { 184 | master-abstract( 185 | doctype: doctype, 186 | degree: degree, 187 | anonymous: anonymous, 188 | twoside: twoside, 189 | ..args, 190 | fonts: fonts + args.named().at("fonts", default: (:)), 191 | info: info + args.named().at("info", default: (:)), 192 | ) 193 | } else if doctype == "postdoc" { 194 | panic("postdoc has not yet been implemented.") 195 | } else { 196 | bachelor-abstract( 197 | anonymous: anonymous, 198 | twoside: twoside, 199 | ..args, 200 | fonts: fonts + args.named().at("fonts", default: (:)), 201 | info: info + args.named().at("info", default: (:)), 202 | ) 203 | } 204 | }, 205 | 206 | // 英文摘要页,通过 type 分发到不同函数 207 | abstract-en: (..args) => { 208 | if doctype == "master" or doctype == "doctor" { 209 | master-abstract-en( 210 | doctype: doctype, 211 | degree: degree, 212 | anonymous: anonymous, 213 | twoside: twoside, 214 | ..args, 215 | fonts: fonts + args.named().at("fonts", default: (:)), 216 | info: info + args.named().at("info", default: (:)), 217 | ) 218 | } else if doctype == "postdoc" { 219 | panic("postdoc has not yet been implemented.") 220 | } else { 221 | bachelor-abstract-en( 222 | anonymous: anonymous, 223 | twoside: twoside, 224 | ..args, 225 | fonts: fonts + args.named().at("fonts", default: (:)), 226 | info: info + args.named().at("info", default: (:)), 227 | ) 228 | } 229 | }, 230 | 231 | // 目录页 232 | outline-page: (..args) => { 233 | bachelor-outline-page( 234 | twoside: twoside, 235 | ..args, 236 | fonts: fonts + args.named().at("fonts", default: (:)), 237 | ) 238 | }, 239 | 240 | // 插图目录页 241 | list-of-figures: (..args) => { 242 | list-of-figures( 243 | twoside: twoside, 244 | ..args, 245 | fonts: fonts + args.named().at("fonts", default: (:)), 246 | ) 247 | }, 248 | 249 | // 表格目录页 250 | list-of-tables: (..args) => { 251 | list-of-tables( 252 | twoside: twoside, 253 | ..args, 254 | fonts: fonts + args.named().at("fonts", default: (:)), 255 | ) 256 | }, 257 | 258 | // 符号表页 259 | notation: (..args) => { 260 | notation( 261 | twoside: twoside, 262 | ..args, 263 | ) 264 | }, 265 | 266 | // 参考文献页 267 | bilingual-bibliography: (..args) => { 268 | bilingual-bibliography( 269 | bibliography: bibliography, 270 | ..args, 271 | ) 272 | }, 273 | 274 | // 致谢页 275 | acknowledgement: (..args) => { 276 | acknowledgement( 277 | anonymous: anonymous, 278 | twoside: twoside, 279 | ..args, 280 | ) 281 | }, 282 | ) 283 | } 284 | -------------------------------------------------------------------------------- /others/bachelor-proposal.typ: -------------------------------------------------------------------------------- 1 | #import "style.typ": 字体, 字号 2 | 3 | #let table-stroke = 0.5pt 4 | 5 | #set page(numbering: "1") 6 | 7 | #align(center, text( 8 | font: 字体.黑体, 9 | size: 字号.三号, 10 | weight: "bold", 11 | "南京大学本科毕业论文(设计)开题报告" 12 | )) 13 | 14 | #set text(font: 字体.宋体, size: 字号.五号) 15 | #set underline(offset: 0.1em) 16 | 17 | #align(center)[ 18 | 填表人签名#underline("      ")填表时间:#underline("    ")年#underline("  ")月#underline("  ")日 19 | ] 20 | 21 | #v(-0.7em) 22 | 23 | #{ 24 | set text(size: 字号.小四) 25 | table( 26 | columns: (100pt, 1fr, 100pt, 1fr), 27 | stroke: table-stroke, 28 | align: center + horizon, 29 | [学生姓名], [], [学号], [], 30 | [院系专业], [], [手机号], [], 31 | [指导教师姓名一 \ (必填)], [], [职称], [], 32 | [导师所在院系], [], [是否校内], [], 33 | [指导教师姓名二 \ (选填)], [], [职称], [], 34 | [导师所在院系], [], [是否校内], [], 35 | [毕设类型], table.cell(colspan: 3)[#sym.ballot.x 毕业论文 #h(1fr) #sym.ballot 毕业设计(含毕业作品) #h(1fr)], 36 | [论文题目], table.cell(colspan: 3)[], 37 | ) 38 | } 39 | 40 | #v(-1.37em) 41 | 42 | #table( 43 | columns: 1fr, 44 | stroke: table-stroke, 45 | inset: 10pt, 46 | )[ 47 | *一、研究背景及意义*(附参考文献) 48 | 49 | #v(45em) 50 | ][ 51 | *二、国内外研究现状*(文献综述,附参考文献,不少于800字) 52 | 53 | #v(45em) 54 | ][ 55 | *三、主要研究或解决的问题和拟采用的方法* 56 | 57 | #v(45em) 58 | ][ 59 | *四、工作进度计划*(每两周为一个单位) 60 | 61 | #v(20em) 62 | ][ 63 | *指导教师意见*(不少于50个字) 64 | 65 | #v(20em) 66 | 67 | #set align(right) 68 | 69 | *签名:* #h(6em) 70 | 71 | #v(1em) 72 | 73 | #strong("    年  月  日") 74 | ][ 75 | *院系意见:* 76 | 77 | *(自动写同意)* 78 | 79 | #v(1fr) 80 | 81 | #set align(right) 82 | 83 | *院系负责人签名:* #h(6em) 84 | 85 | #v(1em) 86 | 87 | #strong("    年  月  日") 88 | ] -------------------------------------------------------------------------------- /others/master-proposal-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/others/master-proposal-logo.png -------------------------------------------------------------------------------- /others/master-proposal.typ: -------------------------------------------------------------------------------- 1 | #import "style.typ": 字体, 字号 2 | 3 | #let table-stroke = 0.5pt 4 | 5 | #set page(numbering: "1") 6 | #set par(leading: 1em) 7 | 8 | // 封面 9 | #{ 10 | set align(center) 11 | 12 | v(5em) 13 | 14 | image("master-proposal-logo.png", width: 7cm) 15 | 16 | v(2em) 17 | 18 | par(text( 19 | font: 字体.黑体, 20 | size: 字号.一号, 21 | weight: "bold", 22 | "某某学院\n硕士研究生论文开题报告", 23 | )) 24 | 25 | v(8em) 26 | 27 | let info-inset = 0pt 28 | 29 | let info-key(body) = { 30 | rect( 31 | width: 100%, 32 | inset: info-inset, 33 | stroke: none, 34 | text(font: 字体.黑体, size: 字号.三号, weight: "bold", body), 35 | ) 36 | } 37 | 38 | let info-value(body) = { 39 | set align(center) 40 | rect( 41 | width: 100%, 42 | inset: info-inset, 43 | stroke: (bottom: table-stroke + black), 44 | text( 45 | font: 字体.黑体, 46 | size: 字号.三号, 47 | bottom-edge: "descender", 48 | body, 49 | ), 50 | ) 51 | } 52 | 53 | grid( 54 | columns: (100pt, 150pt), 55 | column-gutter: -5pt, 56 | row-gutter: 15pt, 57 | info-key[论 文 题 目], info-value[题目], 58 | info-key[作 者 姓 名], info-value[张三], 59 | info-key[作 者 学 号], info-value[1234567890], 60 | info-key[专 业 名 称], info-value[某专业], 61 | info-key[研 究 方 向], info-value[某方向], 62 | info-key[指 导 教 师], info-value[李四], 63 | ) 64 | 65 | v(10em) 66 | 67 | text( 68 | font: 字体.黑体, 69 | size: 字号.三号, 70 | weight: "bold", 71 | "二0  年  月  日", 72 | ) 73 | } 74 | 75 | #pagebreak(weak: true) 76 | 77 | // 主体 78 | #align(center, text( 79 | font: 字体.黑体, 80 | size: 字号.三号, 81 | weight: "bold", 82 | "开题报告", 83 | )) 84 | 85 | #set text(font: 字体.宋体, size: 字号.五号) 86 | #set underline(offset: 0.1em) 87 | 88 | #table( 89 | columns: (2em, 1fr), 90 | align: (center + horizon, auto), 91 | stroke: table-stroke, 92 | inset: 10pt, 93 | )[ 94 | 题 \ \ 目 95 | ][ 96 | #v(5em) 97 | ][ 98 | 题 \ \ 目 \ \ 来 \ \ 源 \ \ 及 \ \ 类 \ \ 型 99 | ][ 100 | #v(25em) 101 | ][ 102 | 研 \ \ 究 \ \ 背 \ \ 景 \ \ 及 \ \ 意 \ \ 义 103 | ][ 104 | #v(1fr) 105 | ][ 106 | 国 \ \ 内 \ \ 外 \ \ 现 \ \ 状 \ \ 及 \ \ 分 \ \ 析 107 | ][ 108 | #v(1fr) 109 | ][ 110 | 研 \ \ 究 \ \ 目 \ \ 标 \ \ 、 \ \ 研 \ \ 究 \ \ 内 \ \ 容 \ \ 和 \ \ 拟 \ \ 解 \ \ 决 \ \ 的 \ \ 关 \ \ 键 \ \ 问 \ \ 题 111 | ][ 112 | #v(1fr) 113 | ][ 114 | 研 \ \ 究 \ \ 方 \ \ 法 \ \ 、 \ \ 设 \ \ 计 \ \ 及 \ \ 试 \ \ 验 \ \ 方 \ \ 案 \ \ 、 \ \ 可 \ \ 行 \ \ 性 \ \ 分 \ \ 析 115 | ][ 116 | #v(1fr) 117 | ][ 118 | 计 \ \ 划 \ \ 进 \ \ 度 \ \ 和 \ \ 质 \ \ 量 \ \ 保 \ \ 证 119 | ][ 120 | #v(1fr) 121 | ][ 122 | 预 \ \ 期 \ \ 成 \ \ 果 \ \ 与 \ \ 创 \ \ 新 \ \ 点 123 | ][ 124 | #v(1fr) 125 | ][ 126 | 参 \ \ 考 \ \ 文 \ \ 献 \ \ ︵ \ \ 不 \ \ 少 \ \ 于 \ \ 20 \ \ 篇 \ \ ︶ 127 | ][ 128 | #v(1fr) 129 | ][ 130 | 导 \ \ 师 \ \ 意 \ \ 见 131 | ][ 132 | #v(28em) 133 | ][ 134 | 考 \ \ 核 \ \ 小 \ \ 组 \ \ 意 \ \ 见 \ \ 及 \ \ 结 \ \ 论 135 | ][ 136 | #v(20em) 137 | 138 | 是否进入论文写作:#h(1em) 是 #sym.ballot #h(2em) 否 #sym.ballot 139 | 140 | // 是否进入论文写作:#h(1em) 是 #sym.ballot.x #h(2em) 否 #sym.ballot 141 | 142 | #v(5em) 143 | 144 | 签字:#underline(" " * 10) #h(1em) #underline(" " * 10) #h(1em) #underline(" " * 10) #h(1em) 145 | 146 | #v(3em) 147 | 148 | 日期:#h(4em) 年 #h(2em) 月 #h(2em) 日 149 | ] 150 | 151 | *注:需向导师提供电子版开题报告,可打印粘贴。* -------------------------------------------------------------------------------- /others/style.typ: -------------------------------------------------------------------------------- 1 | #let 字号 = ( 2 | 初号: 42pt, 3 | 小初: 36pt, 4 | 一号: 26pt, 5 | 小一: 24pt, 6 | 二号: 22pt, 7 | 小二: 18pt, 8 | 三号: 16pt, 9 | 小三: 15pt, 10 | 四号: 14pt, 11 | 中四: 13pt, 12 | 小四: 12pt, 13 | 五号: 10.5pt, 14 | 小五: 9pt, 15 | 六号: 7.5pt, 16 | 小六: 6.5pt, 17 | 七号: 5.5pt, 18 | 小七: 5pt, 19 | ) 20 | 21 | #let 字体 = ( 22 | // 宋体,属于「有衬线字体」,一般可以等同于英文中的 Serif Font 23 | // 这一行分别是「新罗马体(有衬线英文字体)」、「思源宋体(简体)」、「思源宋体」、「宋体(Windows)」、「宋体(MacOS)」 24 | 宋体: ((name: "Times New Roman", covers: "latin-in-cjk"), "Source Han Serif SC", "Source Han Serif", "Noto Serif CJK SC", "SimSun", "Songti SC", "STSongti"), 25 | // 黑体,属于「无衬线字体」,一般可以等同于英文中的 Sans Serif Font 26 | // 这一行分别是「Arial(无衬线英文字体)」、「思源黑体(简体)」、「思源黑体」、「黑体(Windows)」、「黑体(MacOS)」 27 | 黑体: ((name: "Arial", covers: "latin-in-cjk"), "Source Han Sans SC", "Source Han Sans", "Noto Sans CJK SC", "SimHei", "Heiti SC", "STHeiti"), 28 | // 楷体 29 | 楷体: ((name: "Times New Roman", covers: "latin-in-cjk"), "KaiTi", "Kaiti SC", "STKaiti", "FZKai-Z03S"), 30 | // 仿宋 31 | 仿宋: ((name: "Times New Roman", covers: "latin-in-cjk"), "FangSong", "FangSong SC", "STFangSong", "FZFangSong-Z02S"), 32 | // 等宽字体,用于代码块环境,一般可以等同于英文中的 Monospaced Font 33 | // 这一行分别是「Courier New(Windows 等宽英文字体)」、「思源等宽黑体(简体)」、「思源等宽黑体」、「黑体(Windows)」、「黑体(MacOS)」 34 | 等宽: ((name: "Courier New", covers: "latin-in-cjk"), (name: "Menlo", covers: "latin-in-cjk"), (name: "IBM Plex Mono", covers: "latin-in-cjk"), "Source Han Sans HW SC", "Source Han Sans HW", "Noto Sans Mono CJK SC", "SimHei", "Heiti SC", "STHeiti"), 35 | ) -------------------------------------------------------------------------------- /pages/acknowledgement.typ: -------------------------------------------------------------------------------- 1 | // 致谢页 2 | #let acknowledgement( 3 | // documentclass 传入参数 4 | anonymous: false, 5 | twoside: false, 6 | // 其他参数 7 | title: "致谢", 8 | outlined: true, 9 | body, 10 | ) = { 11 | if not anonymous { 12 | pagebreak(weak: true, to: if twoside { "odd" }) 13 | [ 14 | #heading(level: 1, numbering: none, outlined: outlined, title) 15 | 16 | #body 17 | ] 18 | } 19 | } -------------------------------------------------------------------------------- /pages/bachelor-abstract-en.typ: -------------------------------------------------------------------------------- 1 | #import "../utils/custom-cuti.typ": fakebold 2 | #import "../utils/style.typ": 字号, 字体 3 | #import "../utils/double-underline.typ": double-underline 4 | #import "../utils/invisible-heading.typ": invisible-heading 5 | 6 | // 本科生英文摘要页 7 | #let bachelor-abstract-en( 8 | // documentclass 传入的参数 9 | anonymous: false, 10 | twoside: false, 11 | fonts: (:), 12 | info: (:), 13 | // 其他参数 14 | keywords: (), 15 | outline-title: "ABSTRACT", 16 | outlined: false, 17 | anonymous-info-keys: ("author-en", "supervisor-en", "supervisor-ii-en"), 18 | leading: 1.28em, 19 | spacing: 1.38em, 20 | body, 21 | ) = { 22 | // 1. 默认参数 23 | fonts = 字体 + fonts 24 | info = ( 25 | title-en: "NJU Thesis Template for Typst", 26 | author-en: "Zhang San", 27 | department-en: "XX Department", 28 | major-en: "XX Major", 29 | supervisor-en: "Professor Li Si", 30 | ) + info 31 | 32 | // 2. 对参数进行处理 33 | // 2.1 如果是字符串,则使用换行符将标题分隔为列表 34 | if type(info.title-en) == str { 35 | info.title-en = info.title-en.split("\n") 36 | } 37 | 38 | // 3. 内置辅助函数 39 | let info-value(key, body) = { 40 | if not anonymous or (key not in anonymous-info-keys) { 41 | body 42 | } 43 | } 44 | 45 | // 4. 正式渲染 46 | [ 47 | #pagebreak(weak: true, to: if twoside { "odd" }) 48 | 49 | #set text(font: fonts.楷体, size: 字号.小四) 50 | #set par(leading: leading, justify: true, spacing: spacing) 51 | 52 | // 标记一个不可见的标题用于目录生成 53 | #invisible-heading(level: 1, outlined: outlined, outline-title) 54 | 55 | #align(center)[ 56 | #set text(size: 字号.小二, weight: "bold") 57 | 58 | #v(1em) 59 | 60 | #double-underline[#fakebold[南京大学本科生毕业论文(设计、作品)英文摘要]] 61 | ] 62 | 63 | #v(2pt) 64 | 65 | THESIS: #info-value("title-en", (("",)+ info.title-en).sum()) 66 | 67 | DEPARTMENT: #info-value("department-en", info.department-en) 68 | 69 | SPECIALIZATION: #info-value("major-en", info.major-en) 70 | 71 | UNDERGRADUATE: #info-value("author-en", info.author-en) 72 | 73 | MENTOR: #info-value("supervisor-en", info.supervisor-en) #(if info.supervisor-ii-en != "" [#h(1em) #info-value("supervisor-ii-en", info.supervisor-ii-en)]) 74 | 75 | ABSTRACT: #body 76 | 77 | #v(1em) 78 | 79 | KEYWORDS: #(("",)+ keywords.intersperse("; ")).sum() 80 | ] 81 | } -------------------------------------------------------------------------------- /pages/bachelor-abstract.typ: -------------------------------------------------------------------------------- 1 | #import "../utils/custom-cuti.typ": fakebold 2 | #import "../utils/style.typ": 字号, 字体 3 | #import "../utils/double-underline.typ": double-underline 4 | #import "../utils/invisible-heading.typ": invisible-heading 5 | 6 | // 本科生中文摘要页 7 | #let bachelor-abstract( 8 | // documentclass 传入的参数 9 | anonymous: false, 10 | twoside: false, 11 | fonts: (:), 12 | info: (:), 13 | // 其他参数 14 | keywords: (), 15 | outline-title: "中文摘要", 16 | outlined: false, 17 | anonymous-info-keys: ("author", "supervisor", "supervisor-ii"), 18 | leading: 1.28em, 19 | spacing: 1.28em, 20 | body, 21 | ) = { 22 | // 1. 默认参数 23 | fonts = 字体 + fonts 24 | info = ( 25 | title: ("基于 Typst 的", "南京大学学位论文"), 26 | author: "张三", 27 | department: "某学院", 28 | major: "某专业", 29 | supervisor: ("李四", "教授"), 30 | ) + info 31 | 32 | // 2. 对参数进行处理 33 | // 2.1 如果是字符串,则使用换行符将标题分隔为列表 34 | if type(info.title) == str { 35 | info.title = info.title.split("\n") 36 | } 37 | 38 | // 3. 内置辅助函数 39 | let info-value(key, body) = { 40 | if not anonymous or (key not in anonymous-info-keys) { 41 | body 42 | } 43 | } 44 | 45 | // 4. 正式渲染 46 | pagebreak(weak: true, to: if twoside { "odd" }) 47 | 48 | [ 49 | #set text(font: fonts.楷体, size: 字号.小四) 50 | #set par(leading: leading, justify: true, spacing: spacing) 51 | 52 | // 标记一个不可见的标题用于目录生成 53 | #invisible-heading(level: 1, outlined: outlined, outline-title) 54 | 55 | #align(center)[ 56 | #set text(size: 字号.小二, weight: "bold") 57 | 58 | #v(1em) 59 | 60 | #double-underline[#fakebold[南京大学本科生毕业论文(设计、作品)中文摘要]] 61 | ] 62 | 63 | #fakebold[题目:]#info-value("title", (("",)+ info.title).sum()) 64 | 65 | #fakebold[院系:]#info-value("department", info.department) 66 | 67 | #fakebold[专业:]#info-value("major", info.major) 68 | 69 | #fakebold[本科生姓名:]#info-value("author", info.author) 70 | 71 | #fakebold[指导教师(姓名、职称):]#info-value("supervisor", info.supervisor.at(0) + info.supervisor.at(1)) #(if info.supervisor-ii != () [#h(1em) #info-value("supervisor-ii", info.supervisor-ii.at(0) + info.supervisor-ii.at(1))]) 72 | 73 | #fakebold[摘要:] 74 | 75 | #[ 76 | #set par(first-line-indent: (amount: 2em, all: true)) 77 | 78 | #body 79 | ] 80 | 81 | #v(1em) 82 | 83 | #fakebold[关键词:]#(("",)+ keywords.intersperse(";")).sum() 84 | ] 85 | } -------------------------------------------------------------------------------- /pages/bachelor-cover.typ: -------------------------------------------------------------------------------- 1 | #import "../utils/datetime-display.typ": datetime-display 2 | #import "../utils/style.typ": 字号, 字体 3 | 4 | // 本科生封面 5 | #let bachelor-cover( 6 | // documentclass 传入的参数 7 | anonymous: false, 8 | twoside: false, 9 | fonts: (:), 10 | info: (:), 11 | // 其他参数 12 | stoke-width: 0.5pt, 13 | min-title-lines: 2, 14 | info-inset: (x: 0pt, bottom: 1pt), 15 | info-key-width: 72pt, 16 | info-key-font: "楷体", 17 | info-value-font: "楷体", 18 | column-gutter: -3pt, 19 | row-gutter: 11.5pt, 20 | anonymous-info-keys: ("grade", "student-id", "author", "supervisor", "supervisor-ii"), 21 | bold-info-keys: ("title",), 22 | bold-level: "bold", 23 | datetime-display: datetime-display, 24 | ) = { 25 | // 1. 默认参数 26 | fonts = 字体 + fonts 27 | info = ( 28 | title: ("基于 Typst 的", "南京大学学位论文"), 29 | grade: "20XX", 30 | student-id: "1234567890", 31 | author: "张三", 32 | department: "某学院", 33 | major: "某专业", 34 | supervisor: ("李四", "教授"), 35 | submit-date: datetime.today(), 36 | ) + info 37 | 38 | // 2. 对参数进行处理 39 | // 2.1 如果是字符串,则使用换行符将标题分隔为列表 40 | if type(info.title) == str { 41 | info.title = info.title.split("\n") 42 | } 43 | // 2.2 根据 min-title-lines 填充标题 44 | info.title = info.title + range(min-title-lines - info.title.len()).map((it) => " ") 45 | // 2.3 处理提交日期 46 | if type(info.submit-date) == datetime { 47 | info.submit-date = datetime-display(info.submit-date) 48 | } 49 | 50 | // 3. 内置辅助函数 51 | let info-key(body) = { 52 | rect( 53 | width: 100%, 54 | inset: info-inset, 55 | stroke: none, 56 | text( 57 | font: fonts.at(info-key-font, default: "楷体"), 58 | size: 字号.三号, 59 | body 60 | ), 61 | ) 62 | } 63 | 64 | let info-value(key, body) = { 65 | set align(center) 66 | rect( 67 | width: 100%, 68 | inset: info-inset, 69 | stroke: (bottom: stoke-width + black), 70 | text( 71 | font: fonts.at(info-value-font, default: "宋体"), 72 | size: 字号.三号, 73 | weight: if key in bold-info-keys { bold-level } else { "regular" }, 74 | bottom-edge: "descender", 75 | body, 76 | ), 77 | ) 78 | } 79 | 80 | let info-long-value(key, body) = { 81 | grid.cell(colspan: 3, 82 | info-value( 83 | key, 84 | if anonymous and (key in anonymous-info-keys) { 85 | "██████████" 86 | } else { 87 | body 88 | } 89 | ) 90 | ) 91 | } 92 | 93 | let info-short-value(key, body) = { 94 | info-value( 95 | key, 96 | if anonymous and (key in anonymous-info-keys) { 97 | "█████" 98 | } else { 99 | body 100 | } 101 | ) 102 | } 103 | 104 | 105 | // 4. 正式渲染 106 | 107 | pagebreak(weak: true, to: if twoside { "odd" }) 108 | 109 | // 居中对齐 110 | set align(center) 111 | 112 | // 匿名化处理去掉封面标识 113 | if anonymous { 114 | v(52pt) 115 | } else { 116 | // 封面图标 117 | v(6pt) 118 | image("../assets/vi/nju-emblem.svg", width: 2.38cm) 119 | v(22pt) 120 | // 调整一下左边的间距 121 | pad(image("../assets/vi/nju-name.svg", width: 10.5cm), left: 0.4cm) 122 | v(2pt) 123 | } 124 | 125 | // 将中文之间的空格间隙从 0.25 em 调整到 0.5 em 126 | text(size: 字号.一号, font: fonts.宋体, spacing: 200%, weight: "bold")[本 科 毕 业 论 文] 127 | 128 | if anonymous { 129 | v(155pt) 130 | } else { 131 | v(67pt) 132 | } 133 | 134 | block(width: 318pt, grid( 135 | columns: (info-key-width, 1fr, info-key-width, 1fr), 136 | column-gutter: column-gutter, 137 | row-gutter: row-gutter, 138 | info-key("院  系"), 139 | info-long-value("department", info.department), 140 | info-key("专  业"), 141 | info-long-value("major", info.major), 142 | info-key("题  目"), 143 | ..info.title.map((s) => info-long-value("title", s)).intersperse(info-key(" ")), 144 | info-key("年  级"), 145 | info-short-value("grade", info.grade), 146 | info-key("学  号"), 147 | info-short-value("student-id", info.student-id), 148 | info-key("学生姓名"), 149 | info-long-value("author", info.author), 150 | info-key("指导教师"), 151 | info-short-value("supervisor", info.supervisor.at(0)), 152 | info-key("职  称"), 153 | info-short-value("supervisor", info.supervisor.at(1)), 154 | ..(if info.supervisor-ii != () {( 155 | info-key("第二导师"), 156 | info-short-value("supervisor-ii", info.supervisor-ii.at(0)), 157 | info-key("职  称"), 158 | info-short-value("supervisor-ii", info.supervisor-ii.at(1)), 159 | )} else {()}), 160 | info-key("提交日期"), 161 | info-long-value("submit-date", info.submit-date), 162 | )) 163 | } -------------------------------------------------------------------------------- /pages/bachelor-decl-page.typ: -------------------------------------------------------------------------------- 1 | #import "../utils/style.typ": 字号, 字体 2 | 3 | // 本科生声明页 4 | #let bachelor-decl-page( 5 | anonymous: false, 6 | twoside: false, 7 | fonts: (:), 8 | info: (:), 9 | ) = { 10 | // 0. 如果需要匿名则短路返回 11 | if anonymous { 12 | return 13 | } 14 | 15 | // 1. 默认参数 16 | fonts = 字体 + fonts 17 | info = ( 18 | title: ("基于 Typst 的", "南京大学学位论文"), 19 | ) + info 20 | 21 | // 2. 对参数进行处理 22 | // 2.1 如果是字符串,则使用换行符将标题分隔为列表 23 | if type(info.title) == str { 24 | info.title = info.title.split("\n") 25 | } 26 | 27 | 28 | // 3. 正式渲染 29 | pagebreak(weak: true, to: if twoside { "odd" }) 30 | 31 | v(6pt) 32 | 33 | align(center, image("../assets/vi/nju-emblem-purple.svg", width: 1.95cm)) 34 | 35 | v(-12pt) 36 | 37 | align( 38 | center, 39 | text( 40 | font: fonts.黑体, 41 | size: 字号.小一, 42 | weight: "bold", 43 | "南京大学本科毕业论文(设计)\n诚信承诺书", 44 | ), 45 | ) 46 | 47 | v(48pt) 48 | 49 | block[ 50 | #set text(font: fonts.宋体, size: 字号.小三) 51 | #set par(justify: true, first-line-indent: (amount: 2em, all: true), leading: 2.42em) 52 | 53 | 本人郑重承诺:所呈交的毕业论文(设计)(题目:#info.title.sum())是在指导教师的指导下严格按照学校和院系有关规定由本人独立完成的。本毕业论文(设计)中引用他人观点及参考资源的内容均已标注引用,如出现侵犯他人知识产权的行为,由本人承担相应法律责任。本人承诺不存在抄袭、伪造、篡改、代写、买卖毕业论文(设计)等违纪行为。 54 | ] 55 | 56 | v(76pt) 57 | 58 | grid( 59 | columns: (1fr, 150pt), 60 | [], 61 | align(left)[ 62 | #set text(font: fonts.黑体, size: 字号.小三) 63 | 64 | 作者签名: 65 | 66 | 学号: 67 | 68 | 日期: 69 | ] 70 | ) 71 | } -------------------------------------------------------------------------------- /pages/bachelor-outline-page.typ: -------------------------------------------------------------------------------- 1 | #import "../utils/invisible-heading.typ": invisible-heading 2 | #import "../utils/style.typ": 字号, 字体 3 | 4 | // 本科生目录生成 5 | #let bachelor-outline-page( 6 | // documentclass 传入参数 7 | twoside: false, 8 | fonts: (:), 9 | // 其他参数 10 | depth: 4, 11 | title: "目  录", 12 | outlined: false, 13 | title-vspace: 0pt, 14 | title-text-args: auto, 15 | // 引用页数的字体,这里用于显示 Times New Roman 16 | reference-font: auto, 17 | reference-size: 字号.小四, 18 | // 字体与字号 19 | font: auto, 20 | size: (字号.四号, 字号.小四), 21 | // 垂直间距 22 | above: (25pt, 14pt), 23 | below: (14pt, 14pt), 24 | indent: (0pt, 18pt, 28pt), 25 | // 全都显示点号 26 | fill: (repeat([.], gap: 0.15em),), 27 | gap: .3em, 28 | ..args, 29 | ) = { 30 | // 1. 默认参数 31 | fonts = 字体 + fonts 32 | if title-text-args == auto { 33 | title-text-args = (font: fonts.宋体, size: 字号.三号, weight: "bold") 34 | } 35 | // 引用页数的字体,这里用于显示 Times New Roman 36 | if reference-font == auto { 37 | reference-font = fonts.宋体 38 | } 39 | // 字体与字号 40 | if font == auto { 41 | font = (fonts.黑体, fonts.宋体) 42 | } 43 | 44 | // 2. 正式渲染 45 | pagebreak(weak: true, to: if twoside { "odd" }) 46 | 47 | // 默认显示的字体 48 | set text(font: reference-font, size: reference-size) 49 | 50 | { 51 | set align(center) 52 | text(..title-text-args, title) 53 | // 标记一个不可见的标题用于目录生成 54 | invisible-heading(level: 1, outlined: outlined, title) 55 | } 56 | 57 | v(title-vspace) 58 | 59 | // 目录样式 60 | set outline(indent: level => indent.slice(0, calc.min(level + 1, indent.len())).sum()) 61 | show outline.entry: entry => block( 62 | above: above.at(entry.level - 1, default: above.last()), 63 | below: below.at(entry.level - 1, default: below.last()), 64 | link( 65 | entry.element.location(), 66 | entry.indented( 67 | none, 68 | { 69 | text( 70 | font: font.at(entry.level - 1, default: font.last()), 71 | size: size.at(entry.level - 1, default: size.last()), 72 | { 73 | if entry.prefix() not in (none, []) { 74 | entry.prefix() 75 | h(gap) 76 | } 77 | entry.body() 78 | }, 79 | ) 80 | box(width: 1fr, inset: (x: .25em), fill.at(entry.level - 1, default: fill.last())) 81 | entry.page() 82 | }, 83 | gap: 0pt, 84 | ), 85 | ), 86 | ) 87 | 88 | // 显示目录 89 | outline(title: none, depth: depth) 90 | } 91 | -------------------------------------------------------------------------------- /pages/fonts-display-page.typ: -------------------------------------------------------------------------------- 1 | #import "../utils/style.typ": 字号, 字体 2 | #import "../utils/hline.typ": hline 3 | 4 | // 字体显示测试页 5 | #let fonts-display-page( 6 | twoside: false, 7 | fonts: (:), 8 | size: 字号.小四, 9 | lang: "zh", 10 | ) = { 11 | // 1. 默认参数 12 | fonts = 字体 + fonts 13 | 14 | // 2. 辅助函数 15 | let display-font(cjk-name, latin-name) = [ 16 | #set text(font: fonts.at(cjk-name)) 17 | 18 | #cjk-name (#latin-name CJK Regular): 落霞与孤鹜齐飞,秋水共长天一色。 19 | 20 | #cjk-name (#latin-name Latin Regular): The fanfare of birds announces the morning. 21 | 22 | *#cjk-name (#latin-name CJK Bold): 落霞与孤鹜齐飞,秋水共长天一色。* 23 | 24 | *#cjk-name (#latin-name Latin Bold): The fanfare of birds announces the morning.* 25 | ] 26 | 27 | // 3. 正式渲染 28 | pagebreak(weak: true, to: if twoside { "odd" }) 29 | set text(size: size, lang: lang) 30 | 31 | [ 32 | *Fonts Display Page:* 33 | 34 | #hline() 35 | 36 | #display-font("宋体", "SongTi") 37 | 38 | #hline() 39 | 40 | #display-font("黑体", "HeiTi") 41 | 42 | #hline() 43 | 44 | #display-font("楷体", "KaiTi") 45 | 46 | #hline() 47 | 48 | #display-font("仿宋", "FangSong") 49 | 50 | #hline() 51 | 52 | #display-font("等宽", "Mono") 53 | ] 54 | } -------------------------------------------------------------------------------- /pages/list-of-figures.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/i-figured:0.2.4" 2 | #import "../utils/invisible-heading.typ": invisible-heading 3 | #import "../utils/style.typ": 字号, 字体 4 | 5 | // 表格目录生成 6 | #let list-of-figures( 7 | // documentclass 传入参数 8 | twoside: false, 9 | fonts: (:), 10 | // 其他参数 11 | title: "插图目录", 12 | outlined: false, 13 | title-vspace: 32pt, 14 | title-text-args: auto, 15 | // 字体与字号 16 | font: auto, 17 | size: 字号.小四, 18 | // 垂直间距 19 | above: 14pt, 20 | below: 14pt, 21 | ..args, 22 | ) = { 23 | // 1. 默认参数 24 | fonts = 字体 + fonts 25 | if title-text-args == auto { 26 | title-text-args = (font: fonts.宋体, size: 字号.三号, weight: "bold") 27 | } 28 | // 字体与字号 29 | if font == auto { 30 | font = fonts.宋体 31 | } 32 | 33 | // 2. 正式渲染 34 | pagebreak(weak: true, to: if twoside { "odd" }) 35 | 36 | // 默认显示的字体 37 | set text(font: font, size: size) 38 | 39 | { 40 | set align(center) 41 | text(..title-text-args, title) 42 | // 标记一个不可见的标题用于目录生成 43 | invisible-heading(level: 1, outlined: outlined, title) 44 | } 45 | 46 | v(title-vspace) 47 | 48 | show outline.entry: set block( 49 | above: above, 50 | below: below, 51 | ) 52 | 53 | // 显示目录 54 | i-figured.outline(target-kind: image, title: none) 55 | 56 | // 手动分页 57 | if twoside { 58 | pagebreak() + " " 59 | } 60 | } -------------------------------------------------------------------------------- /pages/list-of-tables.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/i-figured:0.1.0" 2 | #import "../utils/invisible-heading.typ": invisible-heading 3 | #import "../utils/style.typ": 字号, 字体 4 | 5 | // 表格目录生成 6 | #let list-of-tables( 7 | // documentclass 传入参数 8 | twoside: false, 9 | fonts: (:), 10 | // 其他参数 11 | title: "表格目录", 12 | outlined: false, 13 | title-vspace: 32pt, 14 | title-text-args: auto, 15 | // 字体与字号 16 | font: auto, 17 | size: 字号.小四, 18 | // 垂直间距 19 | above: 14pt, 20 | below: 14pt, 21 | ..args, 22 | ) = { 23 | // 1. 默认参数 24 | fonts = 字体 + fonts 25 | if title-text-args == auto { 26 | title-text-args = (font: fonts.宋体, size: 字号.三号, weight: "bold") 27 | } 28 | // 字体与字号 29 | if font == auto { 30 | font = fonts.宋体 31 | } 32 | 33 | // 2. 正式渲染 34 | pagebreak(weak: true, to: if twoside { "odd" }) 35 | 36 | // 默认显示的字体 37 | set text(font: font, size: size) 38 | 39 | { 40 | set align(center) 41 | text(..title-text-args, title) 42 | // 标记一个不可见的标题用于目录生成 43 | invisible-heading(level: 1, outlined: outlined, title) 44 | } 45 | 46 | v(title-vspace) 47 | 48 | show outline.entry: set block( 49 | above: above, 50 | below: below, 51 | ) 52 | 53 | // 显示目录 54 | i-figured.outline(target-kind: table, title: none) 55 | 56 | // 手动分页 57 | if twoside { 58 | pagebreak() + " " 59 | } 60 | } -------------------------------------------------------------------------------- /pages/master-abstract-en.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/pinit:0.2.2": pin, pinit-place 2 | #import "../utils/style.typ": 字号, 字体 3 | #import "../utils/double-underline.typ": double-underline 4 | #import "../utils/custom-tablex.typ": gridx, colspanx 5 | #import "../utils/invisible-heading.typ": invisible-heading 6 | 7 | // 研究生英文摘要页 8 | #let master-abstract-en( 9 | // documentclass 传入的参数 10 | doctype: "master", 11 | degree: "academic", 12 | anonymous: false, 13 | twoside: false, 14 | fonts: (:), 15 | info: (:), 16 | // 其他参数 17 | keywords: (), 18 | outline-title: "ABSTRACT", 19 | outlined: true, 20 | abstract-title-weight: "regular", 21 | stoke-width: 0.5pt, 22 | info-value-align: center, 23 | info-inset: (x: 0pt, bottom: 0pt), 24 | info-key-width: 74pt, 25 | grid-inset: 0pt, 26 | column-gutter: 2pt, 27 | row-gutter: 10pt, 28 | anonymous-info-keys: ("author-en", "supervisor-en", "supervisor-ii-en"), 29 | leading: 1.27em, 30 | spacing: 1.27em, 31 | body, 32 | ) = { 33 | // 1. 默认参数 34 | fonts = 字体 + fonts 35 | info = ( 36 | title-en: "NJU Thesis Template for Typst", 37 | author-en: "Zhang San", 38 | department-en: "XX Department", 39 | major-en: "XX Major", 40 | supervisor-en: "Professor Li Si", 41 | ) + info 42 | 43 | // 2. 对参数进行处理 44 | // 2.1 如果是字符串,则使用换行符将标题分隔为列表 45 | if type(info.title-en) == str { 46 | info.title-en = info.title-en.split("\n") 47 | } 48 | 49 | // 3. 内置辅助函数 50 | let info-key(body) = { 51 | rect( 52 | inset: info-inset, 53 | stroke: none, 54 | text(font: fonts.楷体, size: 字号.四号, body), 55 | ) 56 | } 57 | 58 | let info-value(key, body) = { 59 | set align(info-value-align) 60 | rect( 61 | width: 100%, 62 | inset: info-inset, 63 | stroke: (bottom: stoke-width + black), 64 | text( 65 | font: fonts.楷体, 66 | size: 字号.四号, 67 | bottom-edge: "descender", 68 | if anonymous and (key in anonymous-info-keys) { 69 | "█████" 70 | } else { 71 | body 72 | }, 73 | ), 74 | ) 75 | } 76 | 77 | // 4. 正式渲染 78 | pagebreak(weak: true, to: if twoside { "odd" }) 79 | 80 | [ 81 | 82 | #set par(leading: leading, spacing: spacing,justify: true) 83 | 84 | // 标记一个不可见的标题用于目录生成 85 | #invisible-heading(level: 1, outlined: outlined, outline-title) 86 | 87 | #align(center)[ 88 | #set text(font: fonts.楷体,size: 字号.小二, weight: "bold") 89 | 90 | #v(8pt) 91 | 92 | #double-underline((if not anonymous { "南京大学" }) + "研究生毕业论文英文摘要首页用纸") 93 | 94 | #v(-5pt) 95 | ] 96 | 97 | #gridx( 98 | columns: (56pt, auto, auto, 1fr), 99 | inset: grid-inset, 100 | column-gutter: column-gutter, 101 | row-gutter: row-gutter, 102 | info-key[#pin("title-en")THESIS:], colspanx(3, info-value("", " ")), 103 | colspanx(4, info-value("", " ")), 104 | colspanx(3, info-key[SPECIALIZATION:]), info-value("major-en", info.major-en), 105 | colspanx(3, info-key[POSTGRADUATE:]), info-value("author-en", info.author-en), 106 | colspanx(2, info-key[MENTOR:]), colspanx(2, info-value("supervisor-en", info.supervisor-en + if info.supervisor-ii-en != "" { h(1em) + info.supervisor-ii-en })), 107 | ) 108 | 109 | // 用了很 hack 的方法来实现不规则表格长标题换行... 110 | #pinit-place("title-en", { 111 | set text(font: fonts.楷体, size: 字号.四号) 112 | set par(leading: 1.3em) 113 | h(58pt) + (("",)+ info.title-en).intersperse(" ").sum() 114 | }) 115 | 116 | #v(3pt) 117 | 118 | #align(center, text(font: fonts.黑体, size: 字号.小三, weight: abstract-title-weight, "ABSTRACT")) 119 | 120 | #v(-5pt) 121 | 122 | 123 | 124 | #[#set text(font: fonts.楷体, size: 字号.小四) 125 | #set par(first-line-indent: (amount: 2em, all: true)) 126 | 127 | #body 128 | ] 129 | 130 | #v(10pt) 131 | 132 | #[#set text(font: fonts.楷体, size: 字号.小四) 133 | KEYWORDS: #(("",)+ keywords.intersperse("; ")).sum() 134 | ] 135 | ] 136 | } -------------------------------------------------------------------------------- /pages/master-abstract.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/pinit:0.2.2": pin, pinit-place 2 | #import "../utils/style.typ": 字号, 字体 3 | #import "../utils/double-underline.typ": double-underline 4 | #import "../utils/custom-tablex.typ": gridx, colspanx 5 | #import "../utils/invisible-heading.typ": invisible-heading 6 | #import "@preview/cuti:0.3.0": * 7 | // 研究生中文摘要页 8 | #let master-abstract( 9 | // documentclass 传入的参数 10 | doctype: "master", 11 | degree: "academic", 12 | anonymous: false, 13 | twoside: false, 14 | fonts: (:), 15 | info: (:), 16 | // 其他参数 17 | keywords: (), 18 | outline-title: "中文摘要", 19 | outlined: true, 20 | abstract-title-weight: "regular", 21 | stoke-width: 0.5pt, 22 | info-value-align: center, 23 | info-inset: (x: 0pt, bottom: 0pt), 24 | info-key-width: 74pt, 25 | grid-inset: 0pt, 26 | column-gutter: 0pt, 27 | row-gutter: 10pt, 28 | anonymous-info-keys: ("author", "grade", "supervisor", "supervisor-ii"), 29 | leading: 1.27em, 30 | spacing: 1.27em, 31 | body, 32 | ) = { 33 | // 1. 默认参数 34 | fonts = 字体 + fonts 35 | info = ( 36 | title: ("基于 Typst 的", "南京大学学位论文"), 37 | author: "张三", 38 | grade: "20XX", 39 | department: "某学院", 40 | major: "某专业", 41 | supervisor: ("李四", "教授"), 42 | ) + info 43 | 44 | // 2. 对参数进行处理 45 | // 2.1 如果是字符串,则使用换行符将标题分隔为列表 46 | if type(info.title) == str { 47 | info.title = info.title.split("\n") 48 | } 49 | 50 | // 3. 内置辅助函数 51 | let info-key(body) = { 52 | rect( 53 | inset: info-inset, 54 | stroke: none, 55 | text(font: fonts.楷体, size: 字号.四号, body), 56 | ) 57 | } 58 | 59 | let info-value(key, body) = { 60 | set align(info-value-align) 61 | rect( 62 | width: 100%, 63 | inset: info-inset, 64 | stroke: (bottom: stoke-width + black), 65 | text( 66 | font: fonts.楷体, 67 | size: 字号.四号, 68 | bottom-edge: "descender", 69 | if anonymous and (key in anonymous-info-keys) { 70 | "█████" 71 | } else { 72 | body 73 | }, 74 | ), 75 | ) 76 | } 77 | 78 | // 4. 正式渲染 79 | pagebreak(weak: true, to: if twoside { "odd" }) 80 | 81 | [ 82 | // #set text(font: fonts.楷体, size: 字号.四号) 83 | #set par(leading: leading, spacing: spacing,justify: true) 84 | 85 | // 标记一个不可见的标题用于目录生成 86 | #invisible-heading(level: 1, outlined: outlined, outline-title) 87 | 88 | #align(center)[ 89 | #set text(font: fonts.楷体,size: 字号.小二, weight: "bold") 90 | 91 | #v(8pt) 92 | 93 | #double-underline((if not anonymous { "南京大学" }) + "研究生毕业论文中文摘要首页用纸") 94 | 95 | #v(-5pt) 96 | ] 97 | 98 | #gridx( 99 | columns: (104pt, 1fr, auto, 1fr, auto, 1.5fr), 100 | inset: grid-inset, 101 | column-gutter: column-gutter, 102 | row-gutter: row-gutter, 103 | info-key[#pin("title")毕业论文题目:], colspanx(5, info-value("", " ")), 104 | colspanx(6, info-value("", " ")), 105 | colspanx(2, info-value("major", info.major)), info-key[专业], 106 | info-value("grade", info.grade), info-key(if type == "doctor" { [级硕士生姓名:] } else { [级博士生姓名:] } ), info-value("author", info.author), 107 | colspanx(2, info-key[指导教师(姓名、职称):]), colspanx(4, info-value("supervisor", info.supervisor.at(0) + " " + info.supervisor.at(1) + if info.supervisor-ii != () { h(1em) + info.supervisor-ii.at(0) + " " + info.supervisor-ii.at(1) })), 108 | ) 109 | 110 | // 用了很 hack 的方法来实现不规则表格长标题换行... 111 | #pinit-place("title", { 112 | set text(font: fonts.楷体, size: 字号.四号) 113 | set par(leading: 1.3em) 114 | h(108pt) + (("",)+ info.title).sum() 115 | }) 116 | 117 | #v(3pt) 118 | 119 | #align(center, text(font: fonts.黑体, size: 字号.小三, weight: abstract-title-weight, "摘  要")) 120 | 121 | #v(-5pt) 122 | 123 | // #set text(font: fonts.楷体, size: 字号.小四) 124 | 125 | #[#set text(font: fonts.楷体, size: 字号.小四) 126 | #set par(first-line-indent: (amount: 2em, all: true)) 127 | 128 | #body 129 | ] 130 | 131 | #v(10pt) 132 | #[ 133 | #set text(font: fonts.楷体, size: 字号.小四) 134 | #fakebold[关键词]:#(("",)+ keywords.intersperse(";")).sum() 135 | ] 136 | 137 | ] 138 | } -------------------------------------------------------------------------------- /pages/master-cover.typ: -------------------------------------------------------------------------------- 1 | #import "../utils/datetime-display.typ": datetime-display, datetime-en-display 2 | #import "../utils/justify-text.typ": justify-text 3 | #import "../utils/style.typ": 字号, 字体 4 | 5 | // 硕士研究生封面 6 | #let master-cover( 7 | // documentclass 传入的参数 8 | doctype: "master", 9 | degree: "academic", 10 | nl-cover: false, 11 | anonymous: false, 12 | twoside: false, 13 | fonts: (:), 14 | info: (:), 15 | // 其他参数 16 | stoke-width: 0.5pt, 17 | min-title-lines: 2, 18 | min-reviewer-lines: 5, 19 | info-inset: (x: 0pt, bottom: 0.5pt), 20 | info-key-width: 86pt, 21 | info-column-gutter: 18pt, 22 | info-row-gutter: 12pt, 23 | meta-block-inset: (left: -15pt), 24 | meta-info-inset: (x: 0pt, bottom: 2pt), 25 | meta-info-key-width: 35pt, 26 | meta-info-column-gutter: 10pt, 27 | meta-info-row-gutter: 1pt, 28 | defence-info-inset: (x: 0pt, bottom: 0pt), 29 | defence-info-key-width: 110pt, 30 | defence-info-column-gutter: 2pt, 31 | defence-info-row-gutter: 12pt, 32 | anonymous-info-keys: ("student-id", "author", "author-en", "supervisor", "supervisor-en", "supervisor-ii", "supervisor-ii-en", "chairman", "reviewer"), 33 | datetime-display: datetime-display, 34 | datetime-en-display: datetime-en-display, 35 | ) = { 36 | // 1. 默认参数 37 | fonts = 字体 + fonts 38 | info = ( 39 | title: ("基于 Typst 的", "南京大学学位论文"), 40 | grade: "20XX", 41 | student-id: "1234567890", 42 | author: "张三", 43 | department: "某学院", 44 | major: "某专业", 45 | supervisor: ("李四", "教授"), 46 | submit-date: datetime.today(), 47 | ) + info 48 | 49 | // 2. 对参数进行处理 50 | // 2.1 如果是字符串,则使用换行符将标题分隔为列表 51 | if type(info.title) == str { 52 | info.title = info.title.split("\n") 53 | } 54 | if type(info.title-en) == str { 55 | info.title-en = info.title-en.split("\n") 56 | } 57 | // 2.2 根据 min-title-lines 和 min-reviewer-lines 填充标题和评阅人 58 | info.title = info.title + range(min-title-lines - info.title.len()).map((it) => " ") 59 | info.reviewer = info.reviewer + range(min-reviewer-lines - info.reviewer.len()).map((it) => " ") 60 | // 2.3 处理日期 61 | assert(type(info.submit-date) == datetime, message: "submit-date must be datetime.") 62 | if type(info.defend-date) == datetime { 63 | info.defend-date = datetime-display(info.defend-date) 64 | } 65 | if type(info.confer-date) == datetime { 66 | info.confer-date = datetime-display(info.confer-date) 67 | } 68 | if type(info.bottom-date) == datetime { 69 | info.bottom-date = datetime-display(info.bottom-date) 70 | } 71 | // 2.4 处理 degree 72 | if info.degree == auto { 73 | if doctype == "doctor" { 74 | info.degree = "工程博士" 75 | } else { 76 | info.degree = "工程硕士" 77 | } 78 | } 79 | 80 | // 3. 内置辅助函数 81 | let info-key(body, info-inset: info-inset, is-meta: false) = { 82 | set text( 83 | font: if is-meta { fonts.宋体 } else { fonts.楷体 }, 84 | size: if is-meta { 字号.小五 } else { 字号.三号 }, 85 | weight: if is-meta { "regular" } else { "bold" }, 86 | ) 87 | rect( 88 | width: 100%, 89 | inset: info-inset, 90 | stroke: none, 91 | justify-text(with-tail: is-meta, body) 92 | ) 93 | } 94 | 95 | let info-value(key, body, info-inset: info-inset, is-meta: false, no-stroke: false) = { 96 | set align(center) 97 | rect( 98 | width: 100%, 99 | inset: info-inset, 100 | stroke: if no-stroke { none } else { (bottom: stoke-width + black) }, 101 | text( 102 | font: if is-meta { fonts.宋体 } else { fonts.楷体 }, 103 | size: if is-meta { 字号.小五 } else { 字号.三号 }, 104 | bottom-edge: "descender", 105 | if anonymous and (key in anonymous-info-keys) { 106 | if is-meta { "█████" } else { "██████████" } 107 | } else { 108 | body 109 | }, 110 | ), 111 | ) 112 | } 113 | 114 | let anonymous-text(key, body) = { 115 | if anonymous and (key in anonymous-info-keys) { 116 | "██████████" 117 | } else { 118 | body 119 | } 120 | } 121 | 122 | let meta-info-key = info-key.with(info-inset: meta-info-inset, is-meta: true) 123 | let meta-info-value = info-value.with(info-inset: meta-info-inset, is-meta: true) 124 | let defence-info-key = info-key.with(info-inset: defence-info-inset) 125 | let defence-info-value = info-value.with(info-inset: defence-info-inset) 126 | 127 | 128 | // 4. 正式渲染 129 | pagebreak(weak: true, to: if twoside { "odd" }) 130 | 131 | block(width: 70pt, inset: meta-block-inset, grid( 132 | columns: (meta-info-key-width, 1fr), 133 | column-gutter: meta-info-column-gutter, 134 | row-gutter: meta-info-row-gutter, 135 | meta-info-key("学校代码"), 136 | meta-info-value("school-code", info.school-code), 137 | meta-info-key("分类号"), 138 | meta-info-value("clc", info.clc), 139 | meta-info-key("密级"), 140 | meta-info-value("secret-level", info.secret-level), 141 | meta-info-key("UDC"), 142 | meta-info-value("udc", info.udc), 143 | meta-info-key("学号"), 144 | meta-info-value("student-id", info.student-id), 145 | )) 146 | 147 | // 居中对齐 148 | set align(center) 149 | 150 | // 匿名化处理去掉封面标识 151 | if anonymous { 152 | v(70pt) 153 | } else { 154 | // 封面图标 155 | v(6pt) 156 | image("../assets/vi/nju-emblem.svg", width: 1.42cm) 157 | // 调整一下左边的间距 158 | pad(image("../assets/vi/nju-name.svg", width: 4cm)) 159 | v(34pt) 160 | } 161 | 162 | // 将中文之间的空格间隙从 0.25 em 调整到 0.5 em 163 | text(size: 28pt, font: fonts.宋体, spacing: 200%, weight: "bold", 164 | if doctype == "doctor" { "博 士 学 位 论 文" } else { "硕 士 学 位 论 文" }, 165 | ) 166 | 167 | if anonymous { 168 | v(132pt) 169 | } else { 170 | v(30pt) 171 | } 172 | 173 | block(width: 294pt, grid( 174 | columns: (info-key-width, 1fr), 175 | column-gutter: info-column-gutter, 176 | row-gutter: info-row-gutter, 177 | info-key("论文题目"), 178 | ..info.title.map((s) => info-value("title", s)).intersperse(info-key(" ")), 179 | info-key("作者姓名"), 180 | info-value("author", info.author), 181 | ..(if degree == "professional" {( 182 | { 183 | set text(font: fonts.楷体, size: 字号.三号, weight: "bold") 184 | move(dy: 0.3em, scale(x: 55%, box(width: 10em, "专业学位类别(领域)"))) 185 | }, 186 | info-value("major", info.degree + "(" + info.major + ")"), 187 | )} else {( 188 | info-key("专业名称"), 189 | info-value("major", info.major), 190 | )}), 191 | info-key("研究方向"), 192 | info-value("field", info.field), 193 | info-key("导师姓名"), 194 | info-value("supervisor", info.supervisor.intersperse(" ").sum()), 195 | ..(if info.supervisor-ii != () {( 196 | info-key(" "), 197 | info-value("supervisor-ii", info.supervisor-ii.intersperse(" ").sum()), 198 | )} else { () }) 199 | )) 200 | 201 | v(50pt) 202 | 203 | text(font: fonts.楷体, size: 字号.三号, datetime-display(info.submit-date)) 204 | 205 | 206 | // 第二页 207 | pagebreak(weak: true) 208 | 209 | v(161pt) 210 | 211 | block(width: 284pt, grid( 212 | columns: (defence-info-key-width, 1fr), 213 | column-gutter: defence-info-column-gutter, 214 | row-gutter: defence-info-row-gutter, 215 | defence-info-key("答辩委员会主席"), 216 | defence-info-value("chairman", info.chairman), 217 | defence-info-key("评阅人"), 218 | ..info.reviewer.map((s) => defence-info-value("reviewer", s)).intersperse(defence-info-key(" ")), 219 | defence-info-key("论文答辩日期"), 220 | defence-info-value("defend-date", info.defend-date, no-stroke: true), 221 | )) 222 | 223 | v(216pt) 224 | 225 | align(left, box(width: 7.3em, text(font: fonts.楷体, size: 字号.三号, weight: "bold", justify-text(with-tail: true, "研究生签名")))) 226 | 227 | v(7pt) 228 | 229 | align(left, box(width: 7.3em, text(font: fonts.楷体, size: 字号.三号, weight: "bold", justify-text(with-tail: true, "导师签名")))) 230 | 231 | // 第三页英文封面页 232 | pagebreak(weak: true) 233 | 234 | set text(font: fonts.楷体, size: 字号.小四) 235 | set par(leading: 1.3em) 236 | 237 | v(45pt) 238 | 239 | text(font: fonts.黑体, size: 字号.二号, weight: "bold", info.title-en.intersperse("\n").sum()) 240 | 241 | v(36pt) 242 | 243 | text(size: 字号.四号)[by] 244 | 245 | v(-6pt) 246 | 247 | text(font: fonts.黑体, size: 字号.四号, weight: "bold", anonymous-text("author-en", info.author-en)) 248 | 249 | v(11pt) 250 | 251 | text(size: 字号.四号)[Supervised by] 252 | 253 | v(-6pt) 254 | 255 | text(font: fonts.黑体, size: 字号.四号, anonymous-text("supervisor-en", info.supervisor-en)) 256 | 257 | if info.supervisor-ii-en != "" { 258 | v(-4pt) 259 | 260 | text(font: fonts.黑体, size: 字号.四号, anonymous-text("supervisor-ii-en", info.supervisor-ii-en)) 261 | 262 | v(-9pt) 263 | } 264 | 265 | v(26pt) 266 | 267 | [ 268 | A dissertation submitted to \ 269 | the graduate school of #(if not anonymous { "Nanjing University" }) \ 270 | in partial fulfilment of the requirements for the degree of \ 271 | ] 272 | 273 | v(6pt) 274 | 275 | smallcaps(if doctype == "doctor" { "Doctor of phlosophy" } else { "Master" }) 276 | 277 | v(6pt) 278 | 279 | [in] 280 | 281 | v(6pt) 282 | 283 | info.major-en 284 | 285 | v(46pt) 286 | 287 | if not anonymous { 288 | image("../assets/vi/nju-emblem.svg", width: 2.14cm) 289 | } 290 | 291 | v(28pt) 292 | 293 | info.department-en 294 | 295 | v(2pt) 296 | 297 | if not anonymous { 298 | [Nanjing University] 299 | } 300 | 301 | v(28pt) 302 | 303 | datetime-en-display(info.submit-date) 304 | } -------------------------------------------------------------------------------- /pages/master-decl-page.typ: -------------------------------------------------------------------------------- 1 | #import "../utils/style.typ": 字号, 字体 2 | 3 | // 研究生声明页 4 | #let master-decl-page( 5 | anonymous: false, 6 | twoside: false, 7 | fonts: (:), 8 | ) = { 9 | // 0. 如果需要匿名则短路返回 10 | if anonymous { 11 | return 12 | } 13 | 14 | // 1. 默认参数 15 | fonts = 字体 + fonts 16 | 17 | // 2. 正式渲染 18 | pagebreak(weak: true, to: if twoside { "odd" }) 19 | 20 | v(25pt) 21 | 22 | align( 23 | center, 24 | text( 25 | font: fonts.黑体, 26 | size: 字号.四号, 27 | weight: "bold", 28 | "南京大学学位论文原创性声明", 29 | ), 30 | ) 31 | 32 | v(46pt) 33 | 34 | block[ 35 | #set text(font: fonts.宋体, size: 字号.小四) 36 | #set par(justify: true, first-line-indent: (amount: 2em, all: true), leading: 1.2em) 37 | 38 | 本人郑重声明,所提交的学位论文是本人在导师指导下独立进行科学研究工作所取得的成果。除本论文中已经注明引用的内容外,本论文不包含其他个人或集体已经发表或撰写过的研究成果,也不包含为获得南京大学或其他教育机构的学位证书而使用过的材料。对本文的研究做出重要贡献的个人和集体,均已在论文的致谢部分明确标明。本人郑重申明愿承担本声明的法律责任。 39 | ] 40 | 41 | v(143pt) 42 | 43 | align(right)[ 44 | #set text(font: fonts.黑体, size: 字号.小四) 45 | 46 | 研究生签名:#h(5.8em) 47 | 48 | 日期:#h(5.8em) 49 | ] 50 | } -------------------------------------------------------------------------------- /pages/notation.typ: -------------------------------------------------------------------------------- 1 | #let notation( 2 | twoside: false, 3 | title: "符号表", 4 | outlined: true, 5 | width: 350pt, 6 | columns: (60pt, 1fr), 7 | row-gutter: 16pt, 8 | ..args, 9 | body, 10 | ) = { 11 | heading( 12 | level: 1, 13 | numbering: none, 14 | outlined: outlined, 15 | title 16 | ) 17 | 18 | align(center, block(width: width, 19 | align(start, grid( 20 | columns: columns, 21 | row-gutter: row-gutter, 22 | ..args, 23 | // 解析 terms 内部结构以渲染到表格里 24 | ..body.children 25 | .filter(it => it.func() == terms.item) 26 | .map(it => (it.term, it.description)) 27 | .flatten() 28 | )) 29 | )) 30 | 31 | // 手动分页 32 | if twoside { 33 | pagebreak() + " " 34 | } 35 | } -------------------------------------------------------------------------------- /template/ref.bib: -------------------------------------------------------------------------------- 1 | @book{蒋有绪1998, 2 | title={中国森林群落分类及其群落学特征}, 3 | author={蒋有绪 and 郭泉水 and 马娟 and others}, 4 | year={1998}, 5 | publisher={科学出版社}, 6 | address={北京}, 7 | pages={11-12}, 8 | } 9 | 10 | @inproceedings{中国力学学会1990, 11 | title={第3届全国实验流体力学学术会议论文集}, 12 | author={中国力学学会}, 13 | year={1990}, 14 | address={天津}, 15 | publisher={**出版社}, 16 | pages={20-24} 17 | } 18 | 19 | @techreport{WHO1970, 20 | title={World Health Organization. Factors Regulating the Immune Response: Report of WHO Scientific Group}, 21 | year={1970}, 22 | institution={WHO}, 23 | address={Geneva} 24 | } 25 | 26 | @phdthesis{张志祥1998, 27 | author = {张志祥}, 28 | title = {间断动力系统的随机扰动及其在守恒律方程中的应用}, 29 | school = {北京大学数学学院}, 30 | address = {北京}, 31 | year = {1998}, 32 | pages = {50-55} 33 | } 34 | 35 | @patent{河北绿洲2001, 36 | title={一种荒漠化地区生态植被综合培育种植方法:中国,01129210.5}, 37 | author={河北绿洲生态环境科技有限公司}, 38 | year={2001}, 39 | type={patent}, 40 | number={01129210.5}, 41 | note={2001-10-24[2002-05-28]}, 42 | url={http://211.152.9.47/sipoasp/zlijs/hyjs-yxnew. asp?recid=01129210.5&leixin} 43 | } 44 | 45 | @standard{GB/T2659-1986, 46 | title = {世界各国和地区名称代码}, 47 | author = {国家标准局信息分类编码研究所}, 48 | year = {1986}, 49 | howpublished = {全国文献工作标准化技术委员会. 文献工作国家标准汇编:3.北京:中国标准出版社,1988:59-92.}, 50 | note = {GB/T 2659-1986} 51 | } 52 | 53 | 54 | @article{李炳穆2000, 55 | title={理想的图书馆员和信息专家的素质与形象}, 56 | author={李炳穆}, 57 | journal={图书情报工作}, 58 | year={2000}, 59 | volume={2000}, 60 | number={2}, 61 | pages={5-8} 62 | } 63 | 64 | @article{丁文祥2000, 65 | title={数字革命与竞争国际化}, 66 | author={丁文祥}, 67 | journal={中国青年报}, 68 | year={2000}, 69 | month={11-20}, 70 | number={15} 71 | } 72 | 73 | @article{江向东1999, 74 | title={互联网环境下的信息处理与图书管理系统解决方案}, 75 | author={江向东}, 76 | journal={情报学报}, 77 | year={1999}, 78 | volume={18}, 79 | number={2}, 80 | pages={4}, 81 | note={2000-01-18}, 82 | url={http://www.chinainfo.gov.cn/periodical/gbxb/gbxb99/gbxb990203} 83 | } 84 | 85 | @article{CHRISTINE1998, 86 | title={Plant physiology:plant biology in the Genome Era}, 87 | author={M CHRISTINE}, 88 | journal={Science}, 89 | year={1998}, 90 | volume={281}, 91 | pages={331-332}, 92 | note={1998-09-23}, 93 | url={http://www.sciencemag.org/cgi/collection/anatmorp} 94 | } -------------------------------------------------------------------------------- /template/thesis.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/modern-nju-thesis:0.4.0": documentclass 2 | 3 | // 你首先应该安装 https://github.com/nju-lug/modern-nju-thesis/tree/main/fonts/FangZheng 里的所有字体, 4 | // 如果是 Web App 上编辑,你应该手动上传这些字体文件,否则不能正常使用「楷体」和「仿宋」,导致显示错误。 5 | 6 | #let ( 7 | // 布局函数 8 | twoside, doc, preface, mainmatter, appendix, 9 | // 页面函数 10 | fonts-display-page, cover, decl-page, abstract, abstract-en, bilingual-bibliography, 11 | outline-page, list-of-figures, list-of-tables, notation, acknowledgement, 12 | ) = documentclass( 13 | // doctype: "bachelor", // "bachelor" | "master" | "doctor" | "postdoc", 文档类型,默认为本科生 bachelor 14 | // degree: "academic", // "academic" | "professional", 学位类型,默认为学术型 academic 15 | // anonymous: true, // 盲审模式 16 | twoside: true, // 双面模式,会加入空白页,便于打印 17 | // 你会发现 Typst 有许多警告,这是因为 modern-nju-thesis 加入了很多不必要的 fallback 字体 18 | // 你可以自定义字体消除警告,先英文字体后中文字体,应传入「宋体」、「黑体」、「楷体」、「仿宋」、「等宽」 19 | // fonts: (楷体: (name: "Times New Roman", covers: "latin-in-cjk"), "FZKai-Z03S")), 20 | info: ( 21 | title: ("基于 Typst 的", "南京大学学位论文"), 22 | title-en: "My Title in English", 23 | grade: "20XX", 24 | student-id: "1234567890", 25 | author: "张三", 26 | author-en: "Ming Xing", 27 | department: "某学院", 28 | department-en: "School of Chemistry and Chemical Engineering", 29 | major: "某专业", 30 | major-en: "Chemistry", 31 | supervisor: ("李四", "教授"), 32 | supervisor-en: "Professor My Supervisor", 33 | // supervisor-ii: ("王五", "副教授"), 34 | // supervisor-ii-en: "Professor My Supervisor", 35 | submit-date: datetime.today(), 36 | ), 37 | // 参考文献源 38 | bibliography: bibliography.with("ref.bib"), 39 | ) 40 | 41 | // 文稿设置 42 | #show: doc 43 | 44 | // 字体展示测试页 45 | // #fonts-display-page() 46 | 47 | // 封面页 48 | #cover() 49 | 50 | // 声明页 51 | #decl-page() 52 | 53 | 54 | // 前言 55 | #show: preface 56 | 57 | // 中文摘要 58 | #abstract( 59 | keywords: ("我", "就是", "测试用", "关键词") 60 | )[ 61 | 中文摘要 62 | ] 63 | 64 | // 英文摘要 65 | #abstract-en( 66 | keywords: ("Dummy", "Keywords", "Here", "It Is") 67 | )[ 68 | English abstract 69 | ] 70 | 71 | 72 | // 目录 73 | #outline-page() 74 | 75 | // 插图目录 76 | // #list-of-figures() 77 | 78 | // 表格目录 79 | // #list-of-tables() 80 | 81 | // 正文 82 | #show: mainmatter 83 | 84 | // 符号表 85 | // #notation[ 86 | // / DFT: 密度泛函理论 (Density functional theory) 87 | // / DMRG: 密度矩阵重正化群密度矩阵重正化群密度矩阵重正化群 (Density-Matrix Reformation-Group) 88 | // ] 89 | 90 | = 导 论 91 | 92 | == 列表 93 | 94 | === 无序列表 95 | 96 | - 无序列表项一 97 | - 无序列表项二 98 | - 无序子列表项一 99 | - 无序子列表项二 100 | 101 | === 有序列表 102 | 103 | + 有序列表项一 104 | + 有序列表项二 105 | + 有序子列表项一 106 | + 有序子列表项二 107 | 108 | === 术语列表 109 | 110 | / 术语一: 术语解释 111 | / 术语二: 术语解释 112 | 113 | == 图表 114 | 115 | 引用@tbl:timing,引用@tbl:timing-tlt,以及@fig:nju-logo。引用图表时,表格和图片分别需要加上 `tbl:`和`fig:` 前缀才能正常显示编号。 116 | 117 | #align(center, (stack(dir: ltr)[ 118 | #figure( 119 | table( 120 | align: center + horizon, 121 | columns: 4, 122 | [t], [1], [2], [3], 123 | [y], [0.3s], [0.4s], [0.8s], 124 | ), 125 | caption: [常规表], 126 | ) 127 | ][ 128 | #h(50pt) 129 | ][ 130 | #figure( 131 | table( 132 | columns: 4, 133 | stroke: none, 134 | table.hline(), 135 | [t], [1], [2], [3], 136 | table.hline(stroke: .5pt), 137 | [y], [0.3s], [0.4s], [0.8s], 138 | table.hline(), 139 | ), 140 | caption: [三线表], 141 | ) 142 | ])) 143 | 144 | #figure( 145 | image("images/nju-emblem.svg", width: 20%), 146 | caption: [图片测试], 147 | ) 148 | 149 | 150 | == 数学公式 151 | 152 | 可以像 Markdown 一样写行内公式 $x + y$,以及带编号的行间公式: 153 | 154 | $ phi.alt := (1 + sqrt(5)) / 2 $ 155 | 156 | 引用数学公式需要加上 `eqt:` 前缀,则由@eqt:ratio,我们有: 157 | 158 | $ F_n = floor(1 / sqrt(5) phi.alt^n) $ 159 | 160 | 我们也可以通过 `<->` 标签来标识该行间公式不需要编号 161 | 162 | $ y = integral_1^2 x^2 dif x $ <-> 163 | 164 | 而后续数学公式仍然能正常编号。 165 | 166 | $ F_n = floor(1 / sqrt(5) phi.alt^n) $ 167 | 168 | == 参考文献 169 | 170 | 可以像这样引用参考文献:图书#[@蒋有绪1998]和会议#[@中国力学学会1990]。 171 | 172 | == 代码块 173 | 174 | 代码块支持语法高亮。引用时需要加上 `lst:` @lst:code 175 | 176 | #figure( 177 | ```py 178 | def add(x, y): 179 | return x + y 180 | ```, 181 | caption:[代码块], 182 | ) 183 | 184 | 185 | = 正 文 186 | 187 | == 正文子标题 188 | 189 | === 正文子子标题 190 | 191 | 正文内容 192 | 193 | 194 | // 手动分页 195 | #if twoside { 196 | pagebreak() + " " 197 | } 198 | 199 | // 中英双语参考文献 200 | // 默认使用 gb-7714-2015-numeric 样式 201 | #bilingual-bibliography(full: true) 202 | 203 | // 致谢 204 | #acknowledgement[ 205 | 感谢 NJU-LUG,感谢 NJUThesis LaTeX 模板。 206 | ] 207 | 208 | // 手动分页 209 | #if twoside { 210 | pagebreak() + " " 211 | } 212 | 213 | 214 | // 附录 215 | #show: appendix 216 | 217 | = 附录 218 | 219 | == 附录子标题 220 | 221 | === 附录子子标题 222 | 223 | 附录内容,这里也可以加入图片,例如@fig:appendix-img。 224 | 225 | #figure( 226 | image("images/nju-emblem.svg", width: 20%), 227 | caption: [图片测试], 228 | ) 229 | -------------------------------------------------------------------------------- /thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nju-lug/modern-nju-thesis/4a975c8481c946ac644833cf34f1330e9f350429/thumbnail.png -------------------------------------------------------------------------------- /typst.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "modern-nju-thesis" 3 | version = "0.4.0" 4 | entrypoint = "lib.typ" 5 | authors = ["OrangeX4"] 6 | license = "MIT" 7 | description = "南京大学学位论文模板。Modern Nanjing University Thesis." 8 | repository = "https://github.com/nju-lug/modern-nju-thesis" 9 | keywords = ["Nanjing University", "thesis"] 10 | categories = ["thesis"] 11 | exclude = ["imgs"] 12 | compiler = "0.13.0" 13 | 14 | [template] 15 | path = "template" 16 | entrypoint = "thesis.typ" 17 | thumbnail = "thumbnail.png" -------------------------------------------------------------------------------- /utils/bilingual-bibliography.typ: -------------------------------------------------------------------------------- 1 | // Authors: csimide, OrangeX4 2 | // Tested only on GB-7714-2015-Numeric 3 | #let bilingual-bibliography( 4 | bibliography: none, 5 | title: "参考文献", 6 | full: false, 7 | style: "gb-7714-2015-numeric", 8 | mapping: (:), 9 | extra-comma-before-et-al-trans: false, 10 | // 用于控制多位译者时表现为 `et al. tran`(false) 还是 `et al., tran`(true) 11 | allow-comma-in-name: false, 12 | // 如果使用的 CSL 中,英文姓名中会出现逗号,请设置为 true 13 | ) = { 14 | assert(bibliography != none, message: "请传入带有 source 的 bibliography 函数。") 15 | 16 | // Please fill in the remaining mapping table here 17 | mapping = ( 18 | //"等": "et al", 19 | "卷": "Vol.", 20 | "册": "Bk.", 21 | // "译": ", tran", 22 | // "等译": "et al. tran", 23 | // 注: 请见下方译者数量判断部分。 24 | ) + mapping 25 | 26 | let to-string(content) = { 27 | if content.has("text") { 28 | content.text 29 | } else if content.has("children") { 30 | content.children.map(to-string).join("") 31 | } else if content.has("child") { 32 | to-string(content.child) 33 | } else if content.has("body") { 34 | to-string(content.body) 35 | } else if content == [ ] { 36 | " " 37 | } 38 | } 39 | 40 | show grid.cell.where(x: 1): it => { 41 | // 后续的操作是对 string 进行的。 42 | let ittext = to-string(it) 43 | // 判断是否为中文文献:去除特定词组后,仍有至少两个连续汉字。 44 | let pureittext = ittext.replace(regex("[等卷册和版本章期页篇译间者(不详)]"), "") 45 | if pureittext.find(regex("\p{sc=Hani}{2,}")) != none { 46 | // 新增功能:将带有“标准”两个字的一行中的 [Z] 替换为 [S] 47 | ittext = ittext.replace( 48 | regex("标准.*\[Z\]"), 49 | itt => { 50 | itt.text.replace(regex("\[Z\]"), "[S]") 51 | }, 52 | ) 53 | ittext 54 | } else { 55 | // 若不是中文文献,进行替换 56 | // 第xxx卷、第xxx册的情况:变为 Vol. XXX 或 Bk. XXX。 57 | let reptext = ittext 58 | reptext = reptext.replace( 59 | regex("(第\s?)?\d+\s?[卷册]"), 60 | itt => { 61 | if itt.text.contains("卷") { 62 | "Vol. " 63 | } else { 64 | "Bk. " 65 | } 66 | itt.text.find(regex("\d+")) 67 | }, 68 | ) 69 | 70 | // 第xxx版/第xxx本的情况:变为 1st ed 格式。 71 | reptext = reptext.replace( 72 | regex("(第\s?)?\d+\s?[版本]"), 73 | itt => { 74 | let num = itt.text.find(regex("\d+")) 75 | num 76 | if num.clusters().len() == 2 and num.clusters().first() == "1" { 77 | "th" 78 | } else { 79 | ( 80 | "1": "st", 81 | "2": "nd", 82 | "3": "rd", 83 | ).at(num.clusters().last(), default: "th") 84 | } 85 | " ed" 86 | }, 87 | ) 88 | 89 | // 译者数量判断:单数时需要用 trans,复数时需要用 tran 。 90 | /* 91 | 注: 92 | 1. 目前判断译者数量的方法非常草率:有逗号就是多个作者。但是在部分 GB/T 7714-2015 方言中,姓名中可以含有逗号。如果使用的 CSL 是姓名中含有逗号的版本,请将 bilingual-bibliography 的 allow-comma-in-name 参数设为 true。 93 | 2. 在 GB/T 7714-2015 原文中有 `等译`(P15 10.1.3 小节 示例 1-[1] 等),但未给出相应的英文缩写翻译。CSL 社区库内的 GB/T 7714-2015 会使用 `等, 译` 和 `et al., tran` 的写法。为使中英文与标准原文写法一致,本小工具会译作 `et al. tran`。若需要添加逗号,请将 bilingual-bibliography 的 extra-comma-before-et-al-trans 参数设为 true。 94 | 3. GB/T 7714-2015 P8 7.2 小节规定:“译”前需加逗号。因此单个作者的情形,“译” 会被替换为 ", trans"。与“等”并用时的情况请见上一条注。 95 | 如果工作不正常,可以考虑换为简单关键词替换,即注释这段情况,取消 13 行 mapping 内 `译` 条目的注释。 96 | */ 97 | reptext = reptext.replace(regex("\].+?译"), itt => { 98 | // 我想让上面这一行匹配变成非贪婪的,但加问号后没啥效果? 99 | let comma-in-itt = itt.text.replace(regex(",?\s?译"), "").matches(",") 100 | if ( 101 | type(comma-in-itt) == array and 102 | comma-in-itt.len() >= ( 103 | if allow-comma-in-name {2} else {1} 104 | ) 105 | ) { 106 | if extra-comma-before-et-al-trans { 107 | itt.text.replace(regex(",?\s?译"), ", tran") 108 | } else { 109 | itt.text.replace(regex(",?\s?译"), " tran") 110 | } 111 | } else { 112 | itt.text.replace(regex(",?\s?译"), ", trans") 113 | } 114 | }) 115 | 116 | // `等` 特殊处理:`等`后方接内容也需要译作 `et al.`,如 `等译` 需要翻译为 `et al. trans` 117 | reptext = reptext.replace( 118 | regex("等."), 119 | itt => { 120 | "et al." 121 | // 如果原文就是 `等.`,则仅需简单替换,不需要额外处理 122 | // 如果原文 `等` 后没有跟随英文标点,则需要补充一个空格 123 | if not itt.text.last() in (".", ",", ";", ":", "[", "]", "/", "\\", "<", ">", "?", "(", ")", " ", "\"", "'") { 124 | " " 125 | } 126 | // 原文有英文句号时不需要重复句号,否则需要将匹配到的最后一个字符吐回来 127 | if not itt.text.last() == "." { 128 | itt.text.last() 129 | } 130 | }, 131 | ) 132 | 133 | // 其他情况:直接替换 134 | reptext = reptext.replace( 135 | regex("\p{sc=Hani}+"), 136 | itt => { 137 | mapping.at(itt.text, default: itt.text) 138 | // 注意:若替换功能工作良好,应该不会出现 `default` 情形 139 | }, 140 | ) 141 | reptext 142 | } 143 | } 144 | 145 | set text(lang: "zh") 146 | bibliography( 147 | title: title, 148 | full: full, 149 | style: style, 150 | ) 151 | } 152 | -------------------------------------------------------------------------------- /utils/custom-cuti.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/cuti:0.2.1": * -------------------------------------------------------------------------------- /utils/custom-heading.typ: -------------------------------------------------------------------------------- 1 | // 展示一个标题 2 | #let heading-display(it) = { 3 | if it != none { 4 | if it.has("numbering") and it.numbering != none { 5 | numbering(it.numbering, ..counter(heading).at(it.location())) 6 | [ ] 7 | } 8 | it.body 9 | } else { 10 | "" 11 | } 12 | } 13 | 14 | // 获取当前激活的 heading,参数 prev 用于标志优先使用之前页面的 heading 15 | #let active-heading(level: 1, prev: true) = context { 16 | // 之前页面的标题 17 | let prev-headings = query(selector(heading.where(level: level)).before(here())) 18 | // 当前页面的标题 19 | let cur-headings = query(selector(heading.where(level: level)).after(here())) 20 | .filter(it => it.location().page() == loc.page()) 21 | if prev-headings.len() == 0 and cur-headings.len() == 0 { 22 | return none 23 | } else { 24 | if prev { 25 | if prev-headings.len() != 0 { 26 | return prev-headings.last() 27 | } else { 28 | return cur-headings.first() 29 | } 30 | } else { 31 | if cur-headings.len() != 0 { 32 | return cur-headings.first() 33 | } else { 34 | return prev-headings.last() 35 | } 36 | } 37 | } 38 | } 39 | 40 | // 获取当前页面的标题 41 | #let current-heading(level: 1) = context { 42 | // 当前页面的标题 43 | let cur-headings = query(selector(heading.where(level: level)).after(here())) 44 | .filter(it => it.location().page() == here().page()) 45 | if cur-headings.len() != 0 { 46 | return cur-headings.first() 47 | } else { 48 | return none 49 | } 50 | } -------------------------------------------------------------------------------- /utils/custom-numbering.typ: -------------------------------------------------------------------------------- 1 | // 一个简单的自定义 Numbering 2 | // 用法也简单,可以特殊设置一级等标题的样式,以及一个缺省值 3 | #let custom-numbering(base: 1, depth: 5, first-level: auto, second-level: auto, third-level: auto, format, ..args) = { 4 | if args.pos().len() > depth { 5 | return 6 | } 7 | if first-level != auto and args.pos().len() == 1 { 8 | if first-level != "" { 9 | numbering(first-level, ..args) 10 | } 11 | return 12 | } 13 | if second-level != auto and args.pos().len() == 2 { 14 | if second-level != "" { 15 | numbering(second-level, ..args) 16 | } 17 | return 18 | } 19 | if third-level != auto and args.pos().len() == 3 { 20 | if third-level != "" { 21 | numbering(third-level, ..args) 22 | } 23 | return 24 | } 25 | // default 26 | if args.pos().len() >= base { 27 | numbering(format, ..(args.pos().slice(base - 1))) 28 | return 29 | } 30 | } -------------------------------------------------------------------------------- /utils/custom-tablex.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/tablex:0.0.9": * -------------------------------------------------------------------------------- /utils/datetime-display.typ: -------------------------------------------------------------------------------- 1 | // 显示中文日期 2 | #let datetime-display(date) = { 3 | date.display("[year] 年 [month] 月 [day] 日") 4 | } 5 | 6 | // 显示英文日期 7 | #let datetime-en-display(date) = { 8 | date.display("[month repr:short] [day], [year]") 9 | } -------------------------------------------------------------------------------- /utils/double-underline.typ: -------------------------------------------------------------------------------- 1 | // 双下划线 2 | #let double-underline(body) = context { 3 | let size = measure(body) 4 | stack( 5 | body, 6 | v(3pt), 7 | line(length: size.width), 8 | v(2pt), 9 | line(length: size.width), 10 | ) 11 | } -------------------------------------------------------------------------------- /utils/hline.typ: -------------------------------------------------------------------------------- 1 | // 一条水平横线 2 | #let hline() = { 3 | line(length: 100%) 4 | } -------------------------------------------------------------------------------- /utils/invisible-heading.typ: -------------------------------------------------------------------------------- 1 | // 用于创建一个不可见的标题,用于给 outline 加上短标题 2 | #let invisible-heading(..args) = { 3 | set text(size: 0pt, fill: white) 4 | heading(numbering: none, ..args) 5 | } -------------------------------------------------------------------------------- /utils/justify-text.typ: -------------------------------------------------------------------------------- 1 | // 双端对齐一段小文本,常用于表格的中文 key 2 | #let justify-text(with-tail: false, tail: ":", body) = { 3 | if with-tail and tail != "" { 4 | stack(dir: ltr, stack(dir: ltr, spacing: 1fr, ..body.split("").filter(it => it != "")), tail) 5 | } else { 6 | stack(dir: ltr, spacing: 1fr, ..body.split("").filter(it => it != "")) 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /utils/style.typ: -------------------------------------------------------------------------------- 1 | #let 字号 = ( 2 | 初号: 42pt, 3 | 小初: 36pt, 4 | 一号: 26pt, 5 | 小一: 24pt, 6 | 二号: 22pt, 7 | 小二: 18pt, 8 | 三号: 16pt, 9 | 小三: 15pt, 10 | 四号: 14pt, 11 | 中四: 13pt, 12 | 小四: 12pt, 13 | 五号: 10.5pt, 14 | 小五: 9pt, 15 | 六号: 7.5pt, 16 | 小六: 6.5pt, 17 | 七号: 5.5pt, 18 | 小七: 5pt, 19 | ) 20 | 21 | #let 字体 = ( 22 | // 宋体,属于「有衬线字体」,一般可以等同于英文中的 Serif Font 23 | // 这一行分别是「新罗马体(有衬线英文字体)」、「思源宋体(简体)」、「思源宋体」、「宋体(Windows)」、「宋体(MacOS)」 24 | 宋体: ((name: "Times New Roman", covers: "latin-in-cjk"), "Source Han Serif SC", "Source Han Serif", "Noto Serif CJK SC", "SimSun", "Songti SC", "STSongti"), 25 | // 黑体,属于「无衬线字体」,一般可以等同于英文中的 Sans Serif Font 26 | // 这一行分别是「Arial(无衬线英文字体)」、「思源黑体(简体)」、「思源黑体」、「黑体(Windows)」、「黑体(MacOS)」 27 | 黑体: ((name: "Arial", covers: "latin-in-cjk"), "Source Han Sans SC", "Source Han Sans", "Noto Sans CJK SC", "SimHei", "Heiti SC", "STHeiti"), 28 | // 楷体 29 | 楷体: ((name: "Times New Roman", covers: "latin-in-cjk"), "KaiTi", "Kaiti SC", "STKaiti", "FZKai-Z03S"), 30 | // 仿宋 31 | 仿宋: ((name: "Times New Roman", covers: "latin-in-cjk"), "FangSong", "FangSong SC", "STFangSong", "FZFangSong-Z02S"), 32 | // 等宽字体,用于代码块环境,一般可以等同于英文中的 Monospaced Font 33 | // 这一行分别是「Courier New(Windows 等宽英文字体)」、「思源等宽黑体(简体)」、「思源等宽黑体」、「黑体(Windows)」、「黑体(MacOS)」 34 | 等宽: ((name: "Courier New", covers: "latin-in-cjk"), (name: "Menlo", covers: "latin-in-cjk"), (name: "IBM Plex Mono", covers: "latin-in-cjk"), "Source Han Sans HW SC", "Source Han Sans HW", "Noto Sans Mono CJK SC", "SimHei", "Heiti SC", "STHeiti"), 35 | ) -------------------------------------------------------------------------------- /utils/unpairs.typ: -------------------------------------------------------------------------------- 1 | // 将 pairs 数组转成 dict 字典 2 | #let unpairs(pairs) = { 3 | let dict = (:) 4 | for pair in pairs { 5 | dict.insert(..pair) 6 | } 7 | dict 8 | } --------------------------------------------------------------------------------