diff --git a/.gitattributes b/.gitattributes index 544a6ec..9e7dfff 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ -compile.bat text eol=crlf -install_typst.ps1 text eol=crlf \ No newline at end of file +compile.bat text eol=crlf +install_typst.ps1 text eol=crlf +**/*.otf filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3b846fb..812f488 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout master uses: actions/checkout@v3 - with: + with: fetch-depth: 0 - name: Setup Pages uses: actions/configure-pages@v2 @@ -35,7 +35,7 @@ jobs: # run: 'sudo apt-get update && sudo apt install -y ripgrep wget curl && (curl -s https://api.github.com/repos/typst/typst/releases | rg "browser_download_url.*typst-x86_64-unknown-linux-gnu.tar.gz" | head -1 | cut -d : -f 2,3 | tr -d \" | wget -qi -) && tar xf typst-x86_64-unknown-linux-gnu.tar.gz && sudo mv typst-x86_64-unknown-linux-gnu/typst /usr/local/bin/typst' run: 'cargo install --git https://github.com/typst/typst typst-cli' - name: Build PDF - run: 'mkdir build && typst compile --font-path fonts thesis.typ build/thesis.pdf' + run: 'mkdir build && typst compile --root=. --font-path=fonts template/thesis.typ build/thesis.pdf' - name: Upload artifact uses: actions/upload-pages-artifact@v1 with: @@ -64,7 +64,7 @@ jobs: with: name: github-pages - name: Extract artifact - run: | + run: | mkdir -p $RUNNER_TEMP/gitee_pages tar -C $RUNNER_TEMP/gitee_pages -xf artifact.tar - name: Deploy to Gitee Pages @@ -77,7 +77,7 @@ jobs: git config --global user.name "Howard Lau" git config --global user.email "howardlau1999@hotmail.com" - + cd $RUNNER_TEMP/gitee_pages git init git checkout -b gh-pages diff --git a/.github/workflows/gitee.yml b/.github/workflows/gitee.yml deleted file mode 100644 index 738f9e6..0000000 --- a/.github/workflows/gitee.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Sync To Gitee - -on: - push: - branches: - - master - -concurrency: - group: "sync-to-gitee" - cancel-in-progress: true - -jobs: - sync: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - uses: howardlau1999/repository-mirroring-action@main - with: - target_repo_url: - git@gitee.com:howardlau/sysu-thesis-typst.git - ssh_private_key: - ${{ secrets.GITEE_RSA_PRIVATE_KEY }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index a7e151e..14940c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -thesis.pdf \ No newline at end of file +**/*.pdf +.vscode diff --git a/README.md b/README.md index c8cf3cc..7e71fa4 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,9 @@ -# sysu-thesis-typst - -[Gitee 仓库镜像](https://gitee.com/howardlau/sysu-thesis-typst)(自动同步 [GitHub 主仓库](https://github.com/howardlau1999/sysu-thesis-typst),仅供只读访问) - -中山大学学位论文 Typst 模板,项目基于 [PKUTHSS-Typst](https://github.com/lucifer1004/pkuthss-typst) 重构。 +# 基于 Typst 的中山大学学位论文模板 +[![GitLab 仓库](https://img.shields.io/badge/gitlab-%23181717.svg?style=for-the-badge&logo=gitlab&logoColor=white)](https://gitlab.com/sysu-gitlab/thesis-template/better-thesis) +[![GitHub 仓库](https://img.shields.io/badge/gitlab-%23181717.svg?style=for-the-badge&logo=gitlab&logoColor=white)](https://github.com/sysu/better-thesis) 当前还未完全符合学位论文格式要求,欢迎同学们贡献代码!模板交流 QQ 群:797942860([点此直接加入](https://jq.qq.com/?_wv=1027&k=m58va1kd)) -输出成品文件预览(每 6 小时自动更新):[thesis.pdf](https://liuhaohua.com/sysu-thesis-typst/thesis.pdf) - **Q:我不会 LaTeX,可以用这个模板写论文吗?** **A:完全可以!Typst 是一个比 LaTeX 更简单的排版语言,同时安装更加方便,编译更加快速!** @@ -16,6 +12,8 @@ **由于 Typst 还处于初期的快速开发阶段,本项目需要使用从源码编译的 Typst 版本才能正常生成 PDF。仓库提供了一键安装脚本,按照使用说明运行即可。** + + ### Windows 用户 1. [下载本仓库](https://github.com/howardlau1999/sysu-thesis-typst/archive/refs/heads/master.zip),或者使用 `git clone https://github.com/howardlau1999/sysu-thesis-typst` 命令克隆本仓库。 @@ -51,19 +49,4 @@ cargo install --git https://ghproxy.com/https://github.com/typst/typst.git 4. 执行 `make` 命令,即可生成 `thesis.pdf` 文件。 ## 项目结构 - -### 主要文件 - -- `info.typ` 文件中包含论文的基本信息,包括作者、学位、导师、学位论文题目等。 -- `custom.typ` 文件中包含论文的自定义设置,包括行距,字体等。 -- `thesis.typ` 文件是论文的主体,包含论文的各个章节。在添加或者删除章节文件后,需要同步修改这个文件中的 `#include` 命令。 -- `template.typ` 文件是论文的模板,包含论文的各个部分的格式。一般不需要修改。 - -### 文件夹 - -- `chapters` 文件夹包含了论文的各个章节文件,你可以自由地增加或删除章节文件,按你自己需要的方式组织文件。在添加或删除文件后,需要同步修改 `thesis.typ` 文件中的 `#include` 命令。 -- `templates` 文件夹包含了论文的各个部分的模板文件,你可以自由地增加或删除模板文件,按你自己需要的方式组织文件。在添加或删除文件后,需要同步修改 `template.typ` 文件中的 `#include` 命令。欢迎贡献新的模板文件。 -- `functions` 文件夹包含了模板使用到的各种自定义辅助函数,如果你想贡献代码,可以在这个文件夹中添加新的辅助函数。 -- `fonts` 文件夹包含了模板使用到的字体文件,你可以按需要添加或者删除字体文件。在添加或删除文件后,可以运行 `typst --font-path fonts fonts` 查看 Typst 检测到的字体文件。 -- `bibs` 文件夹包含了论文的参考文献文件,你可以按需要添加或者删除参考文献文件,目录结构没有特殊需求。 -- `images` 文件夹包含了论文中使用到的图片文件,你可以按需要添加或者删除图片文件,目录结构没有特殊需求。`vi` 文件夹包含了校徽的矢量图文件。 \ No newline at end of file +详见 `template\thesis.typ` diff --git a/assets/vi/sysu_logo.svg b/assets/vi/sysu_logo.svg new file mode 100644 index 0000000..1c14f8e --- /dev/null +++ b/assets/vi/sysu_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/compile.bat b/compile.bat index 667108f..975c031 100644 --- a/compile.bat +++ b/compile.bat @@ -1,8 +1,7 @@ -@echo off +@echo off chcp 65001>nul -set Path=%cd%\;%Path% set TYPST_FONT_PATHS=%cd%\fonts echo 编译中…… -typst compile --font-path fonts thesis.typ +typst compile --root=%cd% --font-path=fonts template/thesis.typ echo 命令运行完毕,按任意键退出 -pause>nul \ No newline at end of file +pause>nul diff --git a/custom.typ b/custom.typ deleted file mode 100644 index b5878e7..0000000 --- a/custom.typ +++ /dev/null @@ -1,11 +0,0 @@ -#let 行距 = 1em - -#let 字体 = ( - 仿宋: ("Times New Roman", "Adobe Fangsong Std", "FangSong"), - 宋体: ("Times New Roman", "Source Han Serif SC", "SimSun"), - 黑体: ("Times New Roman", "Arial", "Adobe Heiti Std", "SimHei"), - 楷体: ("Times New Roman", "Adobe Kaiti Std R", "KaiTi"), - 代码: ("Inconsolata", "Source Han Sans HW SC", "Times New Roman", "SimSun"), -) - -#let 强调色 = cmyk(100%, 0%, 100%, 60%) \ No newline at end of file diff --git a/fonts/AdobeFonts/AdobeFangSongStd-Regular.otf b/fonts/AdobeFonts/AdobeFangSongStd-Regular.otf index 5f700e1..54bfa28 100644 Binary files a/fonts/AdobeFonts/AdobeFangSongStd-Regular.otf and b/fonts/AdobeFonts/AdobeFangSongStd-Regular.otf differ diff --git a/fonts/AdobeFonts/AdobeHeitiStd-Regular.otf b/fonts/AdobeFonts/AdobeHeitiStd-Regular.otf index 7c4d336..c326cc0 100644 Binary files a/fonts/AdobeFonts/AdobeHeitiStd-Regular.otf and b/fonts/AdobeFonts/AdobeHeitiStd-Regular.otf differ diff --git a/fonts/AdobeFonts/AdobeKaitiStd-Regular.otf b/fonts/AdobeFonts/AdobeKaitiStd-Regular.otf index 5f63bf7..8d168fb 100644 Binary files a/fonts/AdobeFonts/AdobeKaitiStd-Regular.otf and b/fonts/AdobeFonts/AdobeKaitiStd-Regular.otf differ diff --git a/fonts/Arial/Arial.otf b/fonts/Arial/Arial.otf index 1669b0d..f7ebdfe 100644 Binary files a/fonts/Arial/Arial.otf and b/fonts/Arial/Arial.otf differ diff --git a/fonts/Inconsolata/Inconsolata-Bold.otf b/fonts/Inconsolata/Inconsolata-Bold.otf index 572d5d1..39558ab 100644 Binary files a/fonts/Inconsolata/Inconsolata-Bold.otf and b/fonts/Inconsolata/Inconsolata-Bold.otf differ diff --git a/fonts/Inconsolata/Inconsolata-Regular.otf b/fonts/Inconsolata/Inconsolata-Regular.otf index 312475d..9469b11 100644 Binary files a/fonts/Inconsolata/Inconsolata-Regular.otf and b/fonts/Inconsolata/Inconsolata-Regular.otf differ diff --git a/fonts/Inconsolata/Inconsolata-SemiBold.otf b/fonts/Inconsolata/Inconsolata-SemiBold.otf index 7903932..d12e9c8 100644 Binary files a/fonts/Inconsolata/Inconsolata-SemiBold.otf and b/fonts/Inconsolata/Inconsolata-SemiBold.otf differ diff --git a/fonts/SourceHan/SourceHanSansHWSC-Bold.otf b/fonts/SourceHan/SourceHanSansHWSC-Bold.otf index b158cbb..b3a66c2 100644 Binary files a/fonts/SourceHan/SourceHanSansHWSC-Bold.otf and b/fonts/SourceHan/SourceHanSansHWSC-Bold.otf differ diff --git a/fonts/SourceHan/SourceHanSansHWSC-Regular.otf b/fonts/SourceHan/SourceHanSansHWSC-Regular.otf index f46ee46..de11fed 100644 Binary files a/fonts/SourceHan/SourceHanSansHWSC-Regular.otf and b/fonts/SourceHan/SourceHanSansHWSC-Regular.otf differ diff --git a/fonts/SourceHan/SourceHanSerifSC-Bold.otf b/fonts/SourceHan/SourceHanSerifSC-Bold.otf index a7be084..6fee57d 100644 Binary files a/fonts/SourceHan/SourceHanSerifSC-Bold.otf and b/fonts/SourceHan/SourceHanSerifSC-Bold.otf differ diff --git a/fonts/SourceHan/SourceHanSerifSC-Regular.otf b/fonts/SourceHan/SourceHanSerifSC-Regular.otf index 0dd496a..186d841 100644 Binary files a/fonts/SourceHan/SourceHanSerifSC-Regular.otf and b/fonts/SourceHan/SourceHanSerifSC-Regular.otf differ diff --git a/functions/helpers.typ b/functions/helpers.typ index c8b587a..6735ff4 100644 --- a/functions/helpers.typ +++ b/functions/helpers.typ @@ -5,7 +5,7 @@ } else { let ret = () let len = lists.fold( - lists.first().len(), + lists.first().len(), (a, b) => if a > b.len() { b.len() } else { a } ) diff --git a/images/vi/sysu-banner.svg b/images/vi/sysu-banner.svg deleted file mode 100644 index 17be9c7..0000000 --- a/images/vi/sysu-banner.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/images/vi/sysu-emblem.svg b/images/vi/sysu-emblem.svg deleted file mode 100644 index fc54d34..0000000 --- a/images/vi/sysu-emblem.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/layouts/appendix.typ b/layouts/appendix.typ new file mode 100644 index 0000000..ee33f60 --- /dev/null +++ b/layouts/appendix.typ @@ -0,0 +1,24 @@ +#import "@preview/i-figured:0.2.4" +#import "../utils/custom-numbering.typ": custom-numbering + +// 后记,重置 heading 计数器 +#let appendix( + numbering: custom-numbering.with(first-level: "", depth: 4, "1.1 "), + // figure 计数 + show-figure: i-figured.show-figure.with(numbering: "1.1"), + // equation 计数 + show-equation: i-figured.show-equation.with(numbering: "(1.1)"), + // 重置计数 + reset-counter: false, + it, +) = { + set heading(numbering: numbering) + if reset-counter { + counter(heading).update(0) + } + // 设置 figure 的编号 + show figure: show-figure + // 设置 equation 的编号 + show math.equation.where(block: true): show-equation + it +} \ No newline at end of file diff --git a/layouts/doc.typ b/layouts/doc.typ new file mode 100644 index 0000000..011cf8d --- /dev/null +++ b/layouts/doc.typ @@ -0,0 +1,34 @@ +// 文稿设置,可以进行一些像页面边距这类的全局设置 +#let doc( + // documentclass 传入参数 + info: (:), + // 其他参数 + fallback: false, // 字体缺失时使用 fallback,不显示豆腐块 + lang: "zh", + margin: (x: 89pt), + it, +) = { + // 1. 默认参数 + info = ( + title: ("基于 Typst 的", "中山大学学位论文模板"), + author: "张三", + ) + info + + // 2. 对参数进行处理 + // 2.1 如果是字符串,则使用换行符将标题分隔为列表 + if type(info.title) == str { + info.title = info.title.split("\n") + } + + // 3. 基本的样式设置 + set text(fallback: fallback, lang: lang) + set page(margin: margin) + + // 4. PDF 元信息 + set document( + title: (("",)+ info.title).sum(), + author: info.author, + ) + + it +} diff --git a/layouts/mainmatter.typ b/layouts/mainmatter.typ new file mode 100644 index 0000000..b53da8e --- /dev/null +++ b/layouts/mainmatter.typ @@ -0,0 +1,175 @@ +#import "@preview/anti-matter:0.0.2": anti-front-end +#import "@preview/i-figured:0.2.4" +#import "../utils/style.typ": 字号, 字体 +#import "../utils/custom-numbering.typ": custom-numbering +#import "../utils/custom-heading.typ": heading-display, active-heading, current-heading +#import "../utils/indent.typ": fake-par +#import "../utils/unpairs.typ": unpairs + +#let mainmatter( + // documentclass 传入参数 + twoside: false, + fonts: (:), + // 其他参数 + leading: 1.5 * 15.6pt - 0.7em, + spacing: 1.5 * 15.6pt - 0.7em, + justify: true, + first-line-indent: 2em, + numbering: custom-numbering.with(first-level: "第一章 ", depth: 4, "1.1 "), + // 正文字体与字号参数 + text-args: auto, + // 标题字体与字号 + heading-font: (字体.黑体, 字体.黑体, 字体.宋体), + heading-size: (字号.三号, 字号.四号, 字号.小四), + heading-weight: ("regular", "regular", "bold"), + heading-above: (2 * 15.6pt - 0.7em, 2 * 15.6pt - 0.7em), + heading-below: (2 * 15.6pt - 0.7em, 1.5 * 15.6pt - 0.7em), + heading-pagebreak: (true, false), + heading-align: (center, left, auto), + // 页眉 + header-render: auto, + header-vspace: 0em, + display-header: false, + skip-on-first-level: true, + stroke-width: 0.5pt, + reset-footnote: true, + // caption 的 separator + separator: " ", + // caption 样式 + caption-style: strong, + caption-size: 字号.五号, + // figure 计数 + show-figure: i-figured.show-figure, + // equation 计数 + show-equation: i-figured.show-equation, + ..args, + it, +) = { + // 0. 标志前言结束 + anti-front-end() + + // 1. 默认参数 + fonts = 字体 + fonts + if (text-args == auto) { + text-args = (font: fonts.宋体, size: 字号.小四) + } + // 1.1 字体与字号 + if (heading-font == auto) { + heading-font = (fonts.黑体,) + } + // 1.2 处理 heading- 开头的其他参数 + let heading-text-args-lists = args.named().pairs() + .filter((pair) => pair.at(0).starts-with("heading-")) + .map((pair) => (pair.at(0).slice("heading-".len()), pair.at(1))) + + // 2. 辅助函数 + let array-at(arr, pos) = { + arr.at(calc.min(pos, arr.len()) - 1) + } + + // 3. 设置基本样式 + // 3.1 文本和段落样式 + set text(..text-args) + set par( + leading: leading, + justify: justify, + first-line-indent: first-line-indent + ) + show par: set block(spacing: spacing) + // 3.2 脚注样式 + show footnote.entry: set text(font: fonts.宋体, size: 字号.五号) + // 3.3 设置 figure 的编号 + show heading: i-figured.reset-counters + show figure: show-figure + // 3.4 设置 equation 的编号和假段落首行缩进 + show math.equation.where(block: true): show-equation + // 3.5 表格表头置顶 + 不用冒号用空格分割 + 样式 + show figure.where( + kind: table + ): set figure.caption(position: top) + set figure.caption(separator: separator) + show figure.caption: caption-style + show figure.caption: set text(font: fonts.宋体, size: 字号.五号) + // 3.6 优化列表显示 + // 术语列表 terms 不应该缩进 + show terms: set par(first-line-indent: 0pt) + + // 4. 处理标题 + // 4.1 设置标题的 Numbering + set heading(numbering: numbering) + // 4.2 设置字体字号并加入假段落模拟首行缩进 + show heading: it => { + set text( + font: array-at(heading-font, it.level), + size: array-at(heading-size, it.level), + weight: array-at(heading-weight, it.level), + ..unpairs(heading-text-args-lists + .map((pair) => (pair.at(0), array-at(pair.at(1), it.level)))) + ) + set block( + above: array-at(heading-above, it.level), + below: array-at(heading-below, it.level), + ) + it + fake-par + } + // 4.3 标题居中与自动换页 + show heading: it => { + if (array-at(heading-pagebreak, it.level)) { + // 如果打上了 no-auto-pagebreak 标签,则不自动换页 + if ("label" not in it.fields() or str(it.label) != "no-auto-pagebreak") { + pagebreak(weak: true) + } + } + if (array-at(heading-align, it.level) != auto) { + set align(array-at(heading-align, it.level)) + it + } else { + pad(left: 2em, it) + } + } + + // 5. 处理页眉 + set page(..(if display-header { + ( + header: { + // 重置 footnote 计数器 + if reset-footnote { + counter(footnote).update(0) + } + locate(loc => { + // 5.1 获取当前页面的一级标题 + let cur-heading = current-heading(level: 1, loc) + // 5.2 如果当前页面没有一级标题,则渲染页眉 + if not skip-on-first-level or cur-heading == none { + if header-render == auto { + // 一级标题和二级标题 + let first-level-heading = if not twoside or calc.rem(loc.page(), 2) == 0 { heading-display(active-heading(level: 1, loc)) } else { "" } + let second-level-heading = if not twoside or calc.rem(loc.page(), 2) == 2 { heading-display(active-heading(level: 2, prev: false, loc)) } else { "" } + set text(font: fonts.宋体, size: 字号.五号) + stack( + first-level-heading + h(1fr) + second-level-heading, + v(0.25em), + if first-level-heading != "" or second-level-heading != "" { line(length: 100%, stroke: stroke-width + black) }, + ) + } else { + header-render(loc) + } + v(header-vspace) + } + }) + } + ) + } else { + ( + header: { + // 重置 footnote 计数器 + if reset-footnote { + counter(footnote).update(0) + } + } + ) + })) + + it +} diff --git a/layouts/preface.typ b/layouts/preface.typ new file mode 100644 index 0000000..974855e --- /dev/null +++ b/layouts/preface.typ @@ -0,0 +1,18 @@ +#import "@preview/anti-matter:0.0.2": anti-matter + +// 前言,重置页面计数器 +#let preface( + // documentclass 传入的参数 + twoside: false, + // 其他参数 + spec: (front: "I", inner: "1", back: "I"), + ..args, + it, +) = { + // 分页 + if (twoside) { + pagebreak() + " " + } + counter(page).update(0) + anti-matter(spec: spec, ..args, it) +} \ No newline at end of file diff --git a/lib.typ b/lib.typ new file mode 100644 index 0000000..cc7b5b1 --- /dev/null +++ b/lib.typ @@ -0,0 +1,298 @@ +// 中山大学学位论文 Typst 模板 sysu-thesis-typst +// 基于 [南京大学学位论文模板](https://github.com/nju-lug/modern-nju-thesis) 重构中 +// Repo: https://gitlab.com/sysu-gitlab/thesis-template/better-thesis + +#import "layouts/doc.typ": doc +#import "layouts/preface.typ": preface +#import "layouts/mainmatter.typ": mainmatter +#import "layouts/appendix.typ": appendix +#import "pages/fonts-display-page.typ": fonts-display-page +#import "pages/bachelor-cover.typ": bachelor-cover +// #import "pages/master-cover.typ": master-cover +#import "pages/bachelor-title-page.typ": bachelor-title-page +#import "pages/bachelor-decl-page.typ": bachelor-decl-page +// #import "pages/master-decl-page.typ": master-decl-page +#import "pages/bachelor-abstract.typ": bachelor-abstract +// #import "pages/master-abstract.typ": master-abstract +#import "pages/bachelor-abstract-en.typ": bachelor-abstract-en +// #import "pages/master-abstract-en.typ": master-abstract-en +#import "pages/bachelor-outline-page.typ": bachelor-outline-page +#import "pages/list-of-figures.typ": list-of-figures +#import "pages/list-of-tables.typ": list-of-tables +#import "pages/notation.typ": notation +#import "pages/acknowledgement.typ": acknowledgement +#import "utils/bilingual-bibliography.typ": bilingual-bibliography +#import "utils/custom-numbering.typ": custom-numbering +#import "utils/custom-heading.typ": heading-display, active-heading, current-heading +#import "utils/style.typ": 字体, 字号 +#import "@preview/anti-matter:0.0.2": anti-inner-end as mainmatter-end +#import "@preview/i-figured:0.2.4": show-figure, show-equation + +// 使用函数闭包特性,通过 `documentclass` 函数类进行全局信息配置,然后暴露出拥有了全局配置的、具体的 `layouts` 和 `templates` 内部函数。 +#let documentclass( + doctype: "bachelor", // "bachelor" | "master" | "doctor" | "postdoc",文档类型,默认为本科生 bachelor + degree: "academic", // "academic" | "professional",学位类型,默认为学术型 academic + nl-cover: false, // TODO: 是否使用国家图书馆封面,默认关闭 + twoside: false, // 双面模式,会加入空白页,便于打印 + anonymous: false, // 盲审模式 + bibliography: none, // 原来的参考文献函数 + fonts: (:), // 字体,应传入「宋体」、「黑体」、「楷体」、「仿宋」、「等宽」 + info: (:), +) = { + // 默认参数 + fonts = 字体 + fonts + info = ( + title: ("基于 Typst 的", "中山大学学位论文模板"), + title-en: "A Typst Template for SYSU thesis", + grade: "20XX", + student-id: "1234567890", + author: "张三", + author-en: "Zhang San", + department: "某学院", + department-en: "XX Department", + major: "某专业", + major-en: "XX Major", + field: "某方向", + field-en: "XX Field", + supervisor: ("李四", "教授"), + supervisor-en: "Professor Li Si", + supervisor-ii: (), + supervisor-ii-en: "", + submit-date: datetime.today(), + // 以下为研究生项 + defend-date: datetime.today(), + confer-date: datetime.today(), + bottom-date: datetime.today(), + chairman: "某某某 教授", + reviewer: ("某某某 教授", "某某某 教授"), + clc: "O643.12", + udc: "544.4", + secret-level: "公开", + supervisor-contact: "南京大学 江苏省南京市栖霞区仙林大道163号", + email: "xyz@smail.nju.edu.cn", + school-code: "10284", + degree: auto, + degree-en: auto, + ) + info + + ( + // 将传入参数再导出 + doctype: doctype, + degree: degree, + nl-cover: nl-cover, + twoside: twoside, + anonymous: anonymous, + fonts: fonts, + info: info, + // 页面布局 + doc: (..args) => { + doc( + ..args, + info: info + args.named().at("info", default: (:)), + ) + }, + preface: (..args) => { + preface( + twoside: twoside, + ..args, + ) + }, + mainmatter: (..args) => { + if doctype == "master" or doctype == "doctor" { + mainmatter( + twoside: twoside, + display-header: true, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + ) + } else { + mainmatter( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + ) + } + }, + mainmatter-end: (..args) => { + mainmatter-end( + ..args, + ) + }, + appendix: (..args) => { + appendix( + ..args, + ) + }, + + // 字体展示页 + fonts-display-page: (..args) => { + fonts-display-page( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + ) + }, + + // 封面页,通过 type 分发到不同函数 + cover: (..args) => { + if doctype == "master" or doctype == "doctor" { + master-cover( + doctype: doctype, + degree: degree, + nl-cover: nl-cover, + anonymous: anonymous, + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + info: info + args.named().at("info", default: (:)), + ) + } else if doctype == "postdoc" { + panic("postdoc has not yet been implemented.") + } else { + bachelor-cover( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + info: info + args.named().at("info", default: (:)), + ) + } + }, + + // 扉页,通过 type 分发到不同函数 + title-page: (..args) => { + if doctype == "master" or doctype == "doctor" { + panic("master or doctor has not yet been implemented.") + } else if doctype == "postdoc" { + panic("postdoc has not yet been implemented.") + } else { + bachelor-title-page( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + info: info + args.named().at("info", default: (:)), + ) + } + }, + + // 声明页,通过 type 分发到不同函数 + decl-page: (..args) => { + if doctype == "master" or doctype == "doctor" { + master-decl-page( + anonymous: anonymous, + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + ) + } else if doctype == "postdoc" { + panic("postdoc has not yet been implemented.") + } else { + bachelor-decl-page( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + info: info + args.named().at("info", default: (:)), + ) + } + }, + + // 中文摘要页,通过 type 分发到不同函数 + abstract: (..args) => { + if doctype == "master" or doctype == "doctor" { + master-abstract( + doctype: doctype, + degree: degree, + anonymous: anonymous, + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + info: info + args.named().at("info", default: (:)), + ) + } else if doctype == "postdoc" { + panic("postdoc has not yet been implemented.") + } else { + bachelor-abstract( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + info: info + args.named().at("info", default: (:)), + ) + } + }, + + // 英文摘要页,通过 type 分发到不同函数 + abstract-en: (..args) => { + if doctype == "master" or doctype == "doctor" { + master-abstract-en( + doctype: doctype, + degree: degree, + anonymous: anonymous, + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + info: info + args.named().at("info", default: (:)), + ) + } else if doctype == "postdoc" { + panic("postdoc has not yet been implemented.") + } else { + bachelor-abstract-en( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + info: info + args.named().at("info", default: (:)), + ) + } + }, + + // 目录页 + outline-page: (..args) => { + bachelor-outline-page( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + ) + }, + + // 插图目录页 + list-of-figures: (..args) => { + list-of-figures( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + ) + }, + + // 表格目录页 + list-of-tables: (..args) => { + list-of-tables( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + ) + }, + + // 符号表页 + notation: (..args) => { + notation( + twoside: twoside, + ..args, + ) + }, + + // 参考文献页 + bilingual-bibliography: (..args) => { + bilingual-bibliography( + bibliography: bibliography, + ..args, + ) + }, + + // 致谢页 + acknowledgement: (..args) => { + acknowledgement( + anonymous: anonymous, + twoside: twoside, + ..args, + ) + }, + ) +} diff --git a/others/bachelor-proposal.typ b/others/bachelor-proposal.typ new file mode 100644 index 0000000..c861e5e --- /dev/null +++ b/others/bachelor-proposal.typ @@ -0,0 +1,88 @@ +#import "style.typ": 字体, 字号 + +#let table-stroke = 0.5pt + +#set page(numbering: "1") + +#align(center, text( + font: 字体.黑体, + size: 字号.三号, + weight: "bold", + "南京大学本科毕业论文(设计)开题报告" +)) + +#set text(font: 字体.宋体, size: 字号.五号) +#set underline(offset: 0.1em) + +#align(center)[ + 填表人签名#underline("      ")填表时间:#underline("    ")年#underline("  ")月#underline("  ")日 +] + +#v(-0.7em) + +#{ + set text(size: 字号.小四) + table( + columns: (100pt, 1fr, 100pt, 1fr), + stroke: table-stroke, + align: center + horizon, + [学生姓名], [], [学号], [], + [院系专业], [], [手机号], [], + [指导教师姓名一 \ (必填)], [], [职称], [], + [导师所在院系], [], [是否校内], [], + [指导教师姓名二 \ (选填)], [], [职称], [], + [导师所在院系], [], [是否校内], [], + [毕设类型], table.cell(colspan: 3)[#sym.ballot.x 毕业论文 #h(1fr) #sym.ballot 毕业设计(含毕业作品) #h(1fr)], + [论文题目], table.cell(colspan: 3)[], + ) +} + +#v(-1.37em) + +#table( + columns: 1fr, + stroke: table-stroke, + inset: 10pt, +)[ + *一、研究背景及意义*(附参考文献) + + #v(45em) +][ + *二、国内外研究现状*(文献综述,附参考文献,不少于800字) + + #v(45em) +][ + *三、主要研究或解决的问题和拟采用的方法* + + #v(45em) +][ + *四、工作进度计划*(每两周为一个单位) + + #v(20em) +][ + *指导教师意见*(不少于50个字) + + #v(20em) + + #set align(right) + + *签名:* #h(6em) + + #v(1em) + + #strong("    年  月  日") +][ + *院系意见:* + + *(自动写同意)* + + #v(1fr) + + #set align(right) + + *院系负责人签名:* #h(6em) + + #v(1em) + + #strong("    年  月  日") +] \ No newline at end of file diff --git a/others/master-proposal-logo.png b/others/master-proposal-logo.png new file mode 100644 index 0000000..216f87e Binary files /dev/null and b/others/master-proposal-logo.png differ diff --git a/others/master-proposal.typ b/others/master-proposal.typ new file mode 100644 index 0000000..60c5c5b --- /dev/null +++ b/others/master-proposal.typ @@ -0,0 +1,151 @@ +#import "style.typ": 字体, 字号 + +#let table-stroke = 0.5pt + +#set page(numbering: "1") +#set par(leading: 1em) + +// 封面 +#{ + set align(center) + + v(5em) + + image("master-proposal-logo.png", width: 7cm) + + v(2em) + + par(text( + font: 字体.黑体, + size: 字号.一号, + weight: "bold", + "某某学院\n硕士研究生论文开题报告", + )) + + v(8em) + + let info-inset = 0pt + + let info-key(body) = { + rect( + width: 100%, + inset: info-inset, + stroke: none, + text(font: 字体.黑体, size: 字号.三号, weight: "bold", body), + ) + } + + let info-value(body) = { + set align(center) + rect( + width: 100%, + inset: info-inset, + stroke: (bottom: table-stroke + black), + text( + font: 字体.黑体, + size: 字号.三号, + bottom-edge: "descender", + body, + ), + ) + } + + grid( + columns: (100pt, 150pt), + column-gutter: -5pt, + row-gutter: 15pt, + info-key[论 文 题 目], info-value[题目], + info-key[作 者 姓 名], info-value[张三], + info-key[作 者 学 号], info-value[1234567890], + info-key[专 业 名 称], info-value[某专业], + info-key[研 究 方 向], info-value[某方向], + info-key[指 导 教 师], info-value[李四], + ) + + v(10em) + + text( + font: 字体.黑体, + size: 字号.三号, + weight: "bold", + "二0  年  月  日", + ) +} + +#pagebreak(weak: true) + +// 主体 +#align(center, text( + font: 字体.黑体, + size: 字号.三号, + weight: "bold", + "开题报告", +)) + +#set text(font: 字体.宋体, size: 字号.五号) +#set underline(offset: 0.1em) + +#table( + columns: (2em, 1fr), + align: (center + horizon, auto), + stroke: table-stroke, + inset: 10pt, +)[ + 题 \ \ 目 +][ + #v(5em) +][ + 题 \ \ 目 \ \ 来 \ \ 源 \ \ 及 \ \ 类 \ \ 型 +][ + #v(25em) +][ + 研 \ \ 究 \ \ 背 \ \ 景 \ \ 及 \ \ 意 \ \ 义 +][ + #v(1fr) +][ + 国 \ \ 内 \ \ 外 \ \ 现 \ \ 状 \ \ 及 \ \ 分 \ \ 析 +][ + #v(1fr) +][ + 研 \ \ 究 \ \ 目 \ \ 标 \ \ 、 \ \ 研 \ \ 究 \ \ 内 \ \ 容 \ \ 和 \ \ 拟 \ \ 解 \ \ 决 \ \ 的 \ \ 关 \ \ 键 \ \ 问 \ \ 题 +][ + #v(1fr) +][ + 研 \ \ 究 \ \ 方 \ \ 法 \ \ 、 \ \ 设 \ \ 计 \ \ 及 \ \ 试 \ \ 验 \ \ 方 \ \ 案 \ \ 、 \ \ 可 \ \ 行 \ \ 性 \ \ 分 \ \ 析 +][ + #v(1fr) +][ + 计 \ \ 划 \ \ 进 \ \ 度 \ \ 和 \ \ 质 \ \ 量 \ \ 保 \ \ 证 +][ + #v(1fr) +][ + 预 \ \ 期 \ \ 成 \ \ 果 \ \ 与 \ \ 创 \ \ 新 \ \ 点 +][ + #v(1fr) +][ + 参 \ \ 考 \ \ 文 \ \ 献 \ \ ︵ \ \ 不 \ \ 少 \ \ 于 \ \ 20 \ \ 篇 \ \ ︶ +][ + #v(1fr) +][ + 导 \ \ 师 \ \ 意 \ \ 见 +][ + #v(28em) +][ + 考 \ \ 核 \ \ 小 \ \ 组 \ \ 意 \ \ 见 \ \ 及 \ \ 结 \ \ 论 +][ + #v(20em) + + 是否进入论文写作:#h(1em) 是 #sym.ballot #h(2em) 否 #sym.ballot + + // 是否进入论文写作:#h(1em) 是 #sym.ballot.x #h(2em) 否 #sym.ballot + + #v(5em) + + 签字:#underline(" " * 10) #h(1em) #underline(" " * 10) #h(1em) #underline(" " * 10) #h(1em) + + #v(3em) + + 日期:#h(4em) 年 #h(2em) 月 #h(2em) 日 +] + +*注:需向导师提供电子版开题报告,可打印粘贴。* \ No newline at end of file diff --git a/others/style.typ b/others/style.typ new file mode 100644 index 0000000..aa5ce7b --- /dev/null +++ b/others/style.typ @@ -0,0 +1,35 @@ +#let 字号 = ( + 初号: 42pt, + 小初: 36pt, + 一号: 26pt, + 小一: 24pt, + 二号: 22pt, + 小二: 18pt, + 三号: 16pt, + 小三: 15pt, + 四号: 14pt, + 中四: 13pt, + 小四: 12pt, + 五号: 10.5pt, + 小五: 9pt, + 六号: 7.5pt, + 小六: 6.5pt, + 七号: 5.5pt, + 小七: 5pt, +) + +#let 字体 = ( + // 宋体,属于「有衬线字体」,一般可以等同于英文中的 Serif Font + // 这一行分别是「新罗马体(有衬线英文字体)」、「思源宋体(简体)」、「思源宋体」、「宋体(Windows)」、「宋体(MacOS)」 + 宋体: ("Times New Roman", "Source Han Serif SC", "Source Han Serif", "Noto Serif CJK SC", "SimSun", "Songti SC", "STSongti"), + // 黑体,属于「无衬线字体」,一般可以等同于英文中的 Sans Serif Font + // 这一行分别是「Arial(无衬线英文字体)」、「思源黑体(简体)」、「思源黑体」、「黑体(Windows)」、「黑体(MacOS)」 + 黑体: ("Arial", "Source Han Sans SC", "Source Han Sans", "Noto Sans CJK SC", "SimHei", "Heiti SC", "STHeiti"), + // 楷体 + 楷体: ("Times New Roman", "KaiTi", "Kaiti SC", "STKaiti", "FZKai-Z03S"), + // 仿宋 + 仿宋: ("Times New Roman", "FangSong", "FangSong SC", "STFangSong", "FZFangSong-Z02S"), + // 等宽字体,用于代码块环境,一般可以等同于英文中的 Monospaced Font + // 这一行分别是「Courier New(Windows 等宽英文字体)」、「思源等宽黑体(简体)」、「思源等宽黑体」、「黑体(Windows)」、「黑体(MacOS)」 + 等宽: ("Courier New", "Menlo", "IBM Plex Mono", "Source Han Sans HW SC", "Source Han Sans HW", "Noto Sans Mono CJK SC", "SimHei", "Heiti SC", "STHeiti"), +) \ No newline at end of file diff --git a/pages/acknowledgement.typ b/pages/acknowledgement.typ new file mode 100644 index 0000000..28f2ba2 --- /dev/null +++ b/pages/acknowledgement.typ @@ -0,0 +1,19 @@ +// 致谢页 +#let acknowledgement( + // documentclass 传入参数 + anonymous: false, + twoside: false, + // 其他参数 + title: "致谢", + outlined: true, + body, +) = { + if (not anonymous) { + pagebreak(weak: true, to: if twoside { "odd" }) + [ + #heading(level: 1, numbering: none, outlined: outlined, title) + + #body + ] + } +} \ No newline at end of file diff --git a/pages/bachelor-abstract-en.typ b/pages/bachelor-abstract-en.typ new file mode 100644 index 0000000..671d9a8 --- /dev/null +++ b/pages/bachelor-abstract-en.typ @@ -0,0 +1,37 @@ +#import "../utils/style.typ": 字号, 字体 +#import "../utils/invisible-heading.typ": invisible-heading + +// 本科生英文摘要页 +#let bachelor-abstract-en( + // documentclass 传入的参数 + anonymous: false, + twoside: false, + fonts: (:), + info: (:), + // 其他参数 + keywords: (), + outline-title: "ABSTRACT", + outlined: false, + body, +) = { + pagebreak(weak: true, to: if twoside { "odd" }) + + [ + #set text(size: 字号.小四) + + // 标记一个不可见的标题用于目录生成 + #invisible-heading(level: 1, outlined: outlined, outline-title) + + #align(center)[ + #set text(size: 字号.三号, weight: "bold") + [ABSTRACT] + #v(1em) + ] + + #body + + #v(1em) + + *Keywords:* #keywords.join(", ") + ] +} diff --git a/pages/bachelor-abstract.typ b/pages/bachelor-abstract.typ new file mode 100644 index 0000000..811ba86 --- /dev/null +++ b/pages/bachelor-abstract.typ @@ -0,0 +1,40 @@ +#import "../utils/style.typ": 字号, 字体 +#import "../utils/invisible-heading.typ": invisible-heading + +// 本科生中文摘要页 +#let bachelor-abstract( + // documentclass 传入的参数 + twoside: false, + fonts: (:), + info: (:), + // 其他参数 + keywords: (), + outline-title: "中文摘要", + outlined: false, + body, +) = { + pagebreak(weak: true, to: if twoside { "odd" }) + + [ + #set text(font: fonts.宋体, size: 字号.小四) + + // 标记一个不可见的标题用于目录生成 + #invisible-heading(level: 1, outlined: outlined, outline-title) + + #align(center)[ + #set text(size: 字号.三号, font: fonts.黑体) + 【摘#h(1em)要】 + #v(1em) + ] + + #[ + #set par(first-line-indent: 2em) + + #body + ] + + #v(1em) + + *关键词:*#keywords.join(",") + ] +} diff --git a/pages/bachelor-cover.typ b/pages/bachelor-cover.typ new file mode 100644 index 0000000..fc7f355 --- /dev/null +++ b/pages/bachelor-cover.typ @@ -0,0 +1,175 @@ +#import "../utils/datetime-display.typ": datetime-display +#import "../utils/style.typ": 字号, 字体, sysucolor + +// 本科生封面 +#let bachelor-cover( + // documentclass 传入的参数 + twoside: false, + fonts: (:), + info: (:), + // 其他参数 + stoke-width: 0.5pt, + min-title-lines: 2, + info-inset: (x: 0pt, bottom: 1pt), + info-key-width: 72pt, + info-key-font: "黑体", + info-value-font: "宋体", + column-gutter: -3pt, + row-gutter: 11.5pt, + bold-info-keys: ("title",), + bold-level: "bold", +) = { + // 1. 默认参数 + fonts = 字体 + fonts + info = ( + title: ("基于 Typst 的", "中山大学学位论文模板"), + grade: "20XX", + student-id: "1234567890", + author: "张三", + department: "某学院", + major: "某专业", + supervisor: ("李四", "教授"), + submit-date: datetime.today(), + ) + info + + // 2. 对参数进行处理 + // 2.1 如果是字符串,则使用换行符将标题分隔为列表 + if type(info.title) == str { + info.title = info.title.split("\n") + } + // 2.2 根据 min-title-lines 填充标题 + info.title = info.title + range(min-title-lines - info.title.len()).map((it) => " ") + // 2.3 处理提交日期 + if type(info.submit-date) == datetime { + info.submit-date = datetime-display(info.submit-date) + } + + // 3. 内置辅助函数 + let info-key( + font: fonts.at(info-key-font, default: "黑体"), + size: 字号.小三, + body, + ) = { + rect( + width: 100%, + inset: info-inset, + stroke: none, + text( + font: font, + size: size, + body, + ), + ) + } + + let info-value( + font: fonts.at(info-value-font, default: "宋体"), + size: 字号.小三, + key, + body, + ) = { + set align(center) + rect( + width: 100%, + inset: info-inset, + stroke: (bottom: stoke-width + black), + text( + font: font, + size: size, + weight: if (key in bold-info-keys) { bold-level } else { "regular" }, + bottom-edge: "descender", + body, + ), + ) + } + + let info-long-value( + font: fonts.at(info-value-font, default: "宋体"), + size: 字号.小三, + key, + body, + ) = { + grid.cell(colspan: 3, + info-value( + font: font, + size: size, + key, + body, + ) + ) + } + + let info-short-value( + font: fonts.at(info-value-font, default: "宋体"), + size: 字号.小三, + key, + body + ) = { + info-value( + font: font, + size: size, + key, + body, + ) + } + + + // 4. 正式渲染 + + pagebreak(weak: true, to: if twoside { "odd" }) + + // 居中对齐 + set align(center) + + // 封面校徽 + // 使用校方官方 VI 系统的 logo,来源:https://home3.sysu.edu.cn/sysuvi/index.html + image("../assets/vi/sysu_logo.svg", width: 3cm) + + text(size: 字号.小初, font: 字体.宋体, weight: "bold", fill: sysucolor.green)[本科生毕业论文(设计)] + v(-2em) + line(length: 200%, stroke: 0.12cm + sysucolor.green); + v(-0.8em) + line(length: 200%, stroke: 0.05cm + sysucolor.green); + v(1.5cm) + + // 论文题目 + h(0.7cm) + block(width: 100%, grid( + columns: (25%, 1fr, 75%, 1fr), + column-gutter: column-gutter, + row-gutter: row-gutter, + info-key(size: 字号.二号, "题目:"), + ..info.title.map((s) => + info-long-value(size: 字号.二号, font: 字体.黑体, "title", s) + ).intersperse(info-key(size: 字号.二号, " ")), + )) + v(2.7cm) + + // 学生与指导老师信息 + set align(center + bottom) + block(width: 75%, grid( + columns: (info-key-width, 1fr, info-key-width, 1fr), + column-gutter: column-gutter, + row-gutter: row-gutter, + info-key("姓  名"), + info-long-value("author", info.author), + info-key("学  号"), + info-long-value("student-id", info.student-id), + info-key("院  系"), + info-long-value("department", info.department), + info-key("专  业"), + info-long-value("major", info.major), + info-key("指导教师"), + info-long-value("supervisor", text[ #info.supervisor.at(0) #info.supervisor.at(1) ]), + // info-short-value("supervisor", info.supervisor.at(0)), + // info-key("职  称"), + // info-short-value("supervisor", info.supervisor.at(1)), + ..(if info.supervisor-ii != () {( + info-key("第二导师"), + info-long-value("supervisor", text[ #info.supervisor-ii.at(0) #info.supervisor-ii.at(1) ]), + )} else {()}), + )) + v(2em) + + text(font: 字体.黑体, size: 字号.小四)[#info.submit-date] +} diff --git a/pages/bachelor-decl-page.typ b/pages/bachelor-decl-page.typ new file mode 100644 index 0000000..8d364ad --- /dev/null +++ b/pages/bachelor-decl-page.typ @@ -0,0 +1,32 @@ +#import "../utils/indent.typ": indent +#import "../utils/style.typ": 字号, 字体 + +// 本科生声明页 +#let bachelor-decl-page( + anonymous: false, + twoside: false, + fonts: (:), + info: (:), +) = { + pagebreak(weak: true, to: if twoside { "odd" }) + + align(center, text(font: 字体.黑体, size: 字号.三号)[学术诚信声明]) + + set text(font: 字体.宋体, size: 字号.小四) + + par(justify: true, first-line-indent: 2em)[ + 本人郑重声明:所呈交的毕业论文(设计),是本人在导师的指导下,独立进行研究工作所取得的成果。除文中已经注明引用的内容外,本论文(设计)不包含任何其他个人或集体已经发表或撰写过的作品成果。对本论文(设计)的研究做出重要贡献的个人和集体,均已在文中以明确方式标明。本论文(设计)的知识产权归属于培养单位。本人完全意识到本声明的法律结果由本人承担。 + ] + v(2em) + + align(right + top, + box( + grid( + columns: (auto, auto), + gutter: 2em, + "作者签名:", "", + "日" + h(2em) + "期:", h(2.5em) + text("年") + h(2em) + text("月") + h(2em) + text("日") + ) + ) + ) +} diff --git a/pages/bachelor-outline-page.typ b/pages/bachelor-outline-page.typ new file mode 100644 index 0000000..abac887 --- /dev/null +++ b/pages/bachelor-outline-page.typ @@ -0,0 +1,79 @@ +#import "@preview/outrageous:0.1.0" +#import "../utils/invisible-heading.typ": invisible-heading +#import "../utils/style.typ": 字号, 字体 + +// 本科生目录生成 +#let bachelor-outline-page( + // documentclass 传入参数 + twoside: false, + fonts: (:), + // 其他参数 + depth: 4, + title: "目  录", + outlined: false, + title-vspace: 0pt, + title-text-args: auto, + // 引用页数的字体,这里用于显示 Times New Roman + reference-font: auto, + reference-size: 字号.小四, + // 字体与字号 + font: auto, + size: (字号.小四, 字号.小四), + // 垂直间距 + vspace: (25pt, 14pt), + indent: (0pt, 18pt, 28pt), + // 全都显示点号 + fill: (auto,), + ..args, +) = { + // 1. 默认参数 + fonts = 字体 + fonts + if (title-text-args == auto) { + title-text-args = (font: fonts.黑体, size: 字号.三号, weight: "bold") + } + // 引用页数的字体,这里用于显示 Times New Roman + if (reference-font == auto) { + reference-font = fonts.宋体 + } + // 字体与字号 + if (font == auto) { + font = (fonts.宋体, fonts.宋体) + } + + // 2. 正式渲染 + pagebreak(weak: true, to: if twoside { "odd" }) + + // 默认显示的字体 + set text(font: reference-font, size: reference-size) + + { + set align(center) + text(..title-text-args, title) + // 标记一个不可见的标题用于目录生成 + invisible-heading(level: 1, outlined: outlined, title) + } + + v(title-vspace) + + show outline.entry: outrageous.show-entry.with( + // 保留 Typst 基础样式 + ..outrageous.presets.typst, + body-transform: (level, it) => { + // 设置字体和字号 + set text( + font: font.at(calc.min(level, font.len()) - 1), + size: size.at(calc.min(level, size.len()) - 1), + ) + // 计算缩进 + let indent-list = indent + range(level - indent.len()).map((it) => indent.last()) + let indent-length = indent-list.slice(0, count: level).sum() + h(indent-length) + it + }, + vspace: vspace, + fill: fill, + ..args, + ) + + // 显示目录 + outline(title: none, depth: depth) +} diff --git a/pages/bachelor-title-page.typ b/pages/bachelor-title-page.typ new file mode 100644 index 0000000..280aa8b --- /dev/null +++ b/pages/bachelor-title-page.typ @@ -0,0 +1,159 @@ +#import "../utils/datetime-display.typ": datetime-display +#import "../utils/style.typ": 字号, 字体 + +// 本科生论文扉页 +#let bachelor-title-page( + // documentclass 传入的参数 + twoside: false, + fonts: (:), + info: (:), + // 其他参数 + stoke-width: 0.5pt, + min-title-lines: 2, + info-inset: (x: 0pt, bottom: 1pt), + info-key-width: 72pt, + info-key-font: "黑体", + info-value-font: "宋体", + column-gutter: -3pt, + row-gutter: 11.5pt, +) = { + // 1. 默认参数 + fonts = 字体 + fonts + info = ( + title: ("基于 Typst 的", "中山大学学位论文模板"), + title-en: "A Typst Template for SYSU thesis", + grade: "20XX", + student-id: "1234567890", + author: "张三", + department: "某学院", + major: "某专业", + supervisor: ("李四", "教授"), + submit-date: datetime.today(), + ) + info + + // 2. 对参数进行处理 + // 2.1 如果是字符串,则使用换行符将标题分隔为列表 + if type(info.title) == str { + info.title = info.title.split("\n") + } + if type(info.title-en) == str { + info.title-en = info.title-en.split("\n") + } + // 2.2 根据 min-title-lines 填充标题 + info.title = info.title + range(min-title-lines - info.title.len()).map((it) => " ") + // 2.3 处理提交日期 + if type(info.submit-date) == datetime { + info.submit-date = datetime-display(info.submit-date) + } + + // 3. 内置辅助函数 + let info-key( + font: fonts.at(info-key-font, default: "黑体"), + size: 字号.三号, + body, + ) = { + rect( + width: 100%, + inset: info-inset, + stroke: none, + text( + font: font, + size: size, + body, + ), + ) + } + + let info-value( + font: fonts.at(info-value-font, default: "宋体"), + size: 字号.三号, + key, + body, + ) = { + set align(center) + rect( + width: 100%, + inset: info-inset, + stroke: (bottom: stoke-width + black), + text( + font: font, + size: size, + bottom-edge: "descender", + body, + ), + ) + } + + let info-long-value( + font: fonts.at(info-value-font, default: "宋体"), + size: 字号.三号, + key, + body, + ) = { + grid.cell(colspan: 3, + info-value( + font: font, + size: size, + key, + body, + ) + ) + } + + let info-short-value( + font: fonts.at(info-value-font, default: "宋体"), + size: 字号.三号, + key, + body + ) = { + info-value( + font: font, + size: size, + key, + body, + ) + } + + // 4. 正式渲染 + pagebreak(weak: true, to: if twoside { "odd" }) + v(30pt) + + set align(center + horizon) + for part in info.title { + text(size: 字号.二号, font: 字体.黑体, weight: "bold")[ #part ] + linebreak() + } + v(2em) + for part-en in info.title-en { + text(size: 字号.二号, weight: "bold")[ #part-en ] + linebreak() + } + + // 学生与指导老师信息 + set align(center + bottom) + block(width: 75%, grid( + columns: (info-key-width, 1fr, info-key-width, 1fr), + column-gutter: column-gutter, + row-gutter: row-gutter, + info-key("姓  名"), + info-long-value("author", info.author), + info-key("学  号"), + info-long-value("student-id", info.student-id), + info-key("院  系"), + info-long-value("department", info.department), + info-key("专  业"), + info-long-value("major", info.major), + info-key("指导教师"), + info-long-value("supervisor", text[ #info.supervisor.at(0) #info.supervisor.at(1) ]), + // info-short-value("supervisor", info.supervisor.at(0)), + // info-key("职  称"), + // info-short-value("supervisor", info.supervisor.at(1)), + ..(if info.supervisor-ii != () {( + info-key("第二导师"), + info-long-value("supervisor", text[ #info.supervisor-ii.at(0) #info.supervisor-ii.at(1) ]), + )} else {()}), + )) + v(2em) + + text(font: 字体.黑体, size: 字号.四号)[#info.submit-date] +} diff --git a/pages/fonts-display-page.typ b/pages/fonts-display-page.typ new file mode 100644 index 0000000..5b53249 --- /dev/null +++ b/pages/fonts-display-page.typ @@ -0,0 +1,54 @@ +#import "../utils/style.typ": 字号, 字体 +#import "../utils/hline.typ": hline + +// 字体显示测试页 +#let fonts-display-page( + twoside: false, + fonts: (:), + size: 字号.小四, + lang: "zh", +) = { + // 1. 默认参数 + fonts = 字体 + fonts + + // 2. 辅助函数 + let display-font(cjk-name, latin-name) = [ + #set text(font: fonts.at(cjk-name)) + + #cjk-name (#latin-name CJK Regular): 落霞与孤鹜齐飞,秋水共长天一色。 + + #cjk-name (#latin-name Latin Regular): The fanfare of birds announces the morning. + + *#cjk-name (#latin-name CJK Bold): 落霞与孤鹜齐飞,秋水共长天一色。* + + *#cjk-name (#latin-name Latin Bold): The fanfare of birds announces the morning.* + ] + + // 3. 正式渲染 + pagebreak(weak: true, to: if twoside { "odd" }) + set text(size: size, lang: lang) + + [ + *Fonts Display Page:* + + #hline() + + #display-font("宋体", "SongTi") + + #hline() + + #display-font("黑体", "HeiTi") + + #hline() + + #display-font("楷体", "KaiTi") + + #hline() + + #display-font("仿宋", "FangSong") + + #hline() + + #display-font("等宽", "Mono") + ] +} \ No newline at end of file diff --git a/pages/list-of-figures.typ b/pages/list-of-figures.typ new file mode 100644 index 0000000..2eb17cc --- /dev/null +++ b/pages/list-of-figures.typ @@ -0,0 +1,74 @@ +#import "@preview/i-figured:0.2.4" +#import "@preview/outrageous:0.1.0" +#import "../utils/invisible-heading.typ": invisible-heading +#import "../utils/style.typ": 字号, 字体 + +// 表格目录生成 +#let list-of-figures( + // documentclass 传入参数 + twoside: false, + fonts: (:), + // 其他参数 + title: "插图目录", + outlined: false, + title-vspace: 32pt, + title-text-args: auto, + // caption 的 separator + separator: " ", + // 字体与字号 + font: auto, + size: 字号.小四, + // 垂直间距 + vspace: 14pt, + // 是否显示点号 + fill: auto, + ..args, +) = { + // 1. 默认参数 + fonts = 字体 + fonts + if (title-text-args == auto) { + title-text-args = (font: fonts.黑体, size: 字号.三号, weight: "bold") + } + // 字体与字号 + if (font == auto) { + font = fonts.宋体 + } + + // 2. 正式渲染 + pagebreak(weak: true, to: if twoside { "odd" }) + + // 默认显示的字体 + set text(font: font, size: size) + + { + set align(center) + text(..title-text-args, title) + // 标记一个不可见的标题用于目录生成 + invisible-heading(level: 1, outlined: outlined, title) + } + + v(title-vspace) + + show outline.entry: outrageous.show-entry.with( + // 保留 Typst 基础样式 + ..outrageous.presets.typst, + body-transform: (level, it) => { + // 因为好像没找到 separator 的参数,所以这里就手动寻找替换了 + if (it.has("children") and it.children.at(3, default: none) == [#": "]) { + it.children.slice(0, 3).sum() + separator + it.children.slice(4).sum() + } else { + it + } + }, + vspace: (vspace,), + fill: (fill,), + ) + + // 显示目录 + i-figured.outline(target-kind: image, title: none) + + // 手动分页 + if (twoside) { + pagebreak() + " " + } +} diff --git a/pages/list-of-tables.typ b/pages/list-of-tables.typ new file mode 100644 index 0000000..949e47f --- /dev/null +++ b/pages/list-of-tables.typ @@ -0,0 +1,74 @@ +#import "@preview/i-figured:0.1.0" +#import "@preview/outrageous:0.1.0" +#import "../utils/invisible-heading.typ": invisible-heading +#import "../utils/style.typ": 字号, 字体 + +// 表格目录生成 +#let list-of-tables( + // documentclass 传入参数 + twoside: false, + fonts: (:), + // 其他参数 + title: "表格目录", + outlined: false, + title-vspace: 32pt, + title-text-args: auto, + // caption 的 separator + separator: " ", + // 字体与字号 + font: auto, + size: 字号.小四, + // 垂直间距 + vspace: 14pt, + // 是否显示点号 + fill: auto, + ..args, +) = { + // 1. 默认参数 + fonts = 字体 + fonts + if (title-text-args == auto) { + title-text-args = (font: fonts.黑体, size: 字号.三号, weight: "bold") + } + // 字体与字号 + if (font == auto) { + font = fonts.宋体 + } + + // 2. 正式渲染 + pagebreak(weak: true, to: if twoside { "odd" }) + + // 默认显示的字体 + set text(font: font, size: size) + + { + set align(center) + text(..title-text-args, title) + // 标记一个不可见的标题用于目录生成 + invisible-heading(level: 1, outlined: outlined, title) + } + + v(title-vspace) + + show outline.entry: outrageous.show-entry.with( + // 保留 Typst 基础样式 + ..outrageous.presets.typst, + body-transform: (level, it) => { + // 因为好像没找到 separator 的参数,所以这里就手动寻找替换了 + if (it.has("children") and it.children.at(3, default: none) == [#": "]) { + it.children.slice(0, 3).sum() + separator + it.children.slice(4).sum() + } else { + it + } + }, + vspace: (vspace,), + fill: (fill,), + ) + + // 显示目录 + i-figured.outline(target-kind: table, title: none) + + // 手动分页 + if (twoside) { + pagebreak() + " " + } +} diff --git a/pages/notation.typ b/pages/notation.typ new file mode 100644 index 0000000..2b8ac40 --- /dev/null +++ b/pages/notation.typ @@ -0,0 +1,35 @@ +#let notation( + twoside: false, + title: "符号表", + outlined: true, + width: 350pt, + columns: (60pt, 1fr), + row-gutter: 16pt, + ..args, + body, +) = { + heading( + level: 1, + numbering: none, + outlined: outlined, + title + ) + + align(center, block(width: width, + align(start, grid( + columns: columns, + row-gutter: row-gutter, + ..args, + // 解析 terms 内部结构以渲染到表格里 + ..body.children + .filter(it => it.func() == terms.item) + .map(it => (it.term, it.description)) + .flatten() + )) + )) + + // 手动分页 + if (twoside) { + pagebreak() + " " + } +} \ No newline at end of file diff --git a/template.typ b/template.typ index 97653c8..a5a3fb0 100644 --- a/template.typ +++ b/template.typ @@ -241,7 +241,7 @@ } } else if el.func() == heading { // Handle headings - if el.level == 1 { + if el.level == 1 { link(el_loc, chinesenumbering(..counter(heading).at(el_loc), location: el_loc)) } else { link(el_loc, [ @@ -266,7 +266,7 @@ set align(left + top) set text(字号.小四) - include "templates/declaration.typ" + include "templates/declaration.typ" locate(loc => { if alwaysstartodd { @@ -275,7 +275,7 @@ }) include "templates/abstract-cn.typ" - + pagebreak() locate(loc => { @@ -319,7 +319,7 @@ if not blind { par(justify: true, first-line-indent: 2em, leading: 行距)[ #heading(numbering: none, "致谢") - + #include "chapters/acknowledgement.typ" ] diff --git a/template/images/sysu_logo.svg b/template/images/sysu_logo.svg new file mode 100644 index 0000000..1c14f8e --- /dev/null +++ b/template/images/sysu_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/ref.bib b/template/ref.bib new file mode 100644 index 0000000..ac49843 --- /dev/null +++ b/template/ref.bib @@ -0,0 +1,94 @@ +@book{蒋有绪1998, + title={中国森林群落分类及其群落学特征}, + author={蒋有绪 and 郭泉水 and 马娟 and others}, + year={1998}, + publisher={科学出版社}, + address={北京}, + pages={11-12}, +} + +@inproceedings{中国力学学会1990, + title={第3届全国实验流体力学学术会议论文集}, + author={中国力学学会}, + year={1990}, + address={天津}, + publisher={**出版社}, + pages={20-24} +} + +@techreport{WHO1970, + title={World Health Organization. Factors Regulating the Immune Response: Report of WHO Scientific Group}, + year={1970}, + institution={WHO}, + address={Geneva} +} + +@phdthesis{张志祥1998, + author = {张志祥}, + title = {间断动力系统的随机扰动及其在守恒律方程中的应用}, + school = {北京大学数学学院}, + address = {北京}, + year = {1998}, + pages = {50-55} +} + +@patent{河北绿洲2001, + title={一种荒漠化地区生态植被综合培育种植方法:中国,01129210.5}, + author={河北绿洲生态环境科技有限公司}, + year={2001}, + type={patent}, + number={01129210.5}, + note={2001-10-24[2002-05-28]}, + url={http://211.152.9.47/sipoasp/zlijs/hyjs-yxnew. asp?recid=01129210.5&leixin} +} + +@standard{GB/T2659-1986, + title = {世界各国和地区名称代码}, + author = {国家标准局信息分类编码研究所}, + year = {1986}, + howpublished = {全国文献工作标准化技术委员会. 文献工作国家标准汇编:3.北京:中国标准出版社,1988:59-92.}, + note = {GB/T 2659-1986} +} + + +@article{李炳穆2000, + title={理想的图书馆员和信息专家的素质与形象}, + author={李炳穆}, + journal={图书情报工作}, + year={2000}, + volume={2000}, + number={2}, + pages={5-8} +} + +@article{丁文祥2000, + title={数字革命与竞争国际化}, + author={丁文祥}, + journal={中国青年报}, + year={2000}, + month={11-20}, + number={15} +} + +@article{江向东1999, + title={互联网环境下的信息处理与图书管理系统解决方案}, + author={江向东}, + journal={情报学报}, + year={1999}, + volume={18}, + number={2}, + pages={4}, + note={2000-01-18}, + url={http://www.chinainfo.gov.cn/periodical/gbxb/gbxb99/gbxb990203} +} + +@article{CHRISTINE1998, + title={Plant physiology:plant biology in the Genome Era}, + author={M CHRISTINE}, + journal={Science}, + year={1998}, + volume={281}, + pages={331-332}, + note={1998-09-23}, + url={http://www.sciencemag.org/cgi/collection/anatmorp} +} \ No newline at end of file diff --git a/template/thesis.typ b/template/thesis.typ new file mode 100644 index 0000000..8ae10b1 --- /dev/null +++ b/template/thesis.typ @@ -0,0 +1,240 @@ +// TODO: 待正式发布时替换成可用的版本 +// 建议在正式编辑论文时,采用 typst.app 中的已发布版本模板 +// #import "@preview/morden-sysu-thesis:0.1.0": documentclass + +// 仅供开发调试使用 +#import "../lib.typ": documentclass + +// 你首先应该安装 https://gitlab.com/sysu-gitlab/thesis-template/better-thesis/-/tree/main/fonts 里的所有字体, +// 如果是 Web App 上编辑,你应该手动上传这些字体文件,否则不能正常使用「楷体」和「仿宋」,导致显示错误。 + +#let ( + // 布局函数 + twoside, doc, preface, mainmatter, mainmatter-end, appendix, + // 页面函数 + fonts-display-page, cover, title-page, decl-page, abstract, abstract-en, bilingual-bibliography, + outline-page, list-of-figures, list-of-tables, notation, acknowledgement, +) = documentclass( + // doctype: "bachelor", // "bachelor" | "master" | "doctor" | "postdoc", 文档类型,默认为本科生 bachelor + // degree: "academic", // "academic" | "professional", 学位类型,默认为学术型 academic + // anonymous: true, // 盲审模式 + twoside: false, // 双面模式,会加入空白页,便于打印 + // 可自定义字体,先英文字体后中文字体,应传入「宋体」、「黑体」、「楷体」、「仿宋」、「等宽」 + // fonts: (楷体: ("Times New Roman", "FZKai-Z03S")), + info: ( + title: ("基于 Typst 的", "中山大学学位论文模板"), + title-en: "A Typst Template for SYSU thesis", + grade: "20XX", + student-id: "1234567890", + author: "张三", + author-en: "Ming Xing", + department: "某学院", + department-en: "School of Chemistry and Chemical Engineering", + major: "某专业", + major-en: "Chemistry", + supervisor: ("李四", "教授"), + supervisor-en: "Professor My Supervisor", + // supervisor-ii: ("王五", "副教授"), + // supervisor-ii-en: "Professor My Supervisor", + submit-date: datetime.today(), + ), + // 参考文献源 + bibliography: bibliography.with("ref.bib"), +) + +// 文稿设置 +#show: doc + +// 字体展示测试页 +#fonts-display-page() + +// 封面页 +#cover() + +// 扉页 +#title-page() + +// 声明页 +#decl-page() + + +// 前言 +#show: preface + +// 中文摘要 +#abstract( + keywords: ("我", "就是", "测试用", "关键词") +)[ + 中文摘要 +] + +// 英文摘要 +#abstract-en( + keywords: ("Dummy", "Keywords", "Here", "It Is") +)[ + English abstract +] + + +// 目录 +#outline-page() + +// 插图目录 +// #list-of-figures() + +// 表格目录 +// #list-of-tables() + +// 正文 +#show: mainmatter + +// 符号表 +// #notation[ +// / DFT: 密度泛函理论 (Density functional theory) +// / DMRG: 密度矩阵重正化群密度矩阵重正化群密度矩阵重正化群 (Density-Matrix Reformation-Group) +// ] + += 导 论 + +== 列表 + +=== 无序列表 + +- 无序列表项一 +- 无序列表项二 + - 无序子列表项一 + - 无序子列表项二 + +=== 有序列表 + ++ 有序列表项一 ++ 有序列表项二 + + 有序子列表项一 + + 有序子列表项二 + +=== 术语列表 + +/ 术语一: 术语解释 +/ 术语二: 术语解释 + +== 图表 + +引用@tbl:timing,引用@tbl:timing-tlt,以及@fig:sysu-logo。引用图表时,表格和图片分别需要加上 `tbl:`和`fig:` 前缀才能正常显示编号。 + +#align(center, (stack(dir: ltr)[ + #figure( + table( + align: center + horizon, + columns: 4, + [t], [1], [2], [3], + [y], [0.3s], [0.4s], [0.8s], + ), + caption: [常规表], + ) +][ + #h(50pt) +][ + #figure( + table( + columns: 4, + stroke: none, + table.hline(), + [t], [1], [2], [3], + table.hline(stroke: .5pt), + [y], [0.3s], [0.4s], [0.8s], + table.hline(), + ), + caption: [三线表], + ) +])) + +#figure( + image("images/sysu_logo.svg", width: 20%), + caption: [图片测试], +) + + +== 数学公式 + +可以像 Markdown 一样写行内公式 $x + y$,以及带编号的行间公式: + +$ phi.alt := (1 + sqrt(5)) / 2 $ + +引用数学公式需要加上 `eqt:` 前缀,则由@eqt:ratio,我们有: + +$ F_n = floor(1 / sqrt(5) phi.alt^n) $ + +我们也可以通过 `<->` 标签来标识该行间公式不需要编号 + +$ y = integral_1^2 x^2 dif x $ <-> + +而后续数学公式仍然能正常编号。 + +$ F_n = floor(1 / sqrt(5) phi.alt^n) $ + +== 参考文献 + +可以像这样引用参考文献:图书#[@蒋有绪1998]和会议#[@中国力学学会1990]。 + +== 代码块 + +代码块支持语法高亮。引用时需要加上 `lst:` @lst:code + +#figure( + ```py + def add(x, y): + return x + y + ```, + caption:[代码块], +) + + += 正 文 + +== 正文子标题 + +=== 正文子子标题 + +正文内容 + + +// 手动分页 +#if twoside { + pagebreak() + " " +} + +// 中英双语参考文献 +// 默认使用 gb-7714-2015-numeric 样式 +#bilingual-bibliography(full: true) + +// 致谢 +#acknowledgement[ + 感谢 NJU-LUG,感谢 NJUThesis LaTeX 模板。 +] + +// 手动分页 +#if twoside { + pagebreak() + " " +} + + +// 附录 +#show: appendix + += 附录 + +== 附录子标题 + +=== 附录子子标题 + +附录内容,这里也可以加入图片,例如@fig:appendix-img。 + +#figure( + image("images/sysu_logo.svg", width: 20%), + caption: [图片测试], +) + + +// 正文结束标志,不可缺少 +// 这里放在附录后面,使得页码能正确计数 +#mainmatter-end() diff --git a/templates/abstract-cn.typ b/templates/abstract-cn.typ deleted file mode 100644 index c49d672..0000000 --- a/templates/abstract-cn.typ +++ /dev/null @@ -1,16 +0,0 @@ -#import "../functions/style.typ": * -#import "../custom.typ": * -#import "../info.typ": * - -#par(justify: true, first-line-indent: 2em, leading: 行距)[ - #heading(numbering: none, outlined: false, "摘要") - - #include "../chapters/abstract-cn.typ" - #import "../chapters/abstract-cn.typ": 中文关键词 - - #linebreak() - #set par(first-line-indent: 0em) - #text("关键词:", weight: "bold") - #中文关键词.join(",") - #v(2em) -] \ No newline at end of file diff --git a/templates/abstract-en.typ b/templates/abstract-en.typ deleted file mode 100644 index c2694eb..0000000 --- a/templates/abstract-en.typ +++ /dev/null @@ -1,27 +0,0 @@ -#import "../functions/style.typ": * -#import "../custom.typ": * -#import "../info.typ": * - -#par(justify: true, first-line-indent: 2em, leading: 行距)[ - // #[ - // #set text(字号.小二) - // #set align(center) - // #strong(论文英文题目) - // ] - // #[ - // #set align(center) - // #英文作者名 \(#英文专业\) \ - // Directed by #英文导师名 - // ] - #heading(numbering: none, outlined: false, "ABSTRACT") - - #include "../chapters/abstract-en.typ" - #import "../chapters/abstract-en.typ": 英文关键词 - - #linebreak() - #set par(first-line-indent: 0em) - #text("Keywords:", weight: "bold") - #h(0.5em, weak: true) - #英文关键词.join(", ") - #v(2em) -] \ No newline at end of file diff --git a/templates/cover-no-emblem.typ b/templates/cover-no-emblem.typ deleted file mode 100644 index d981329..0000000 --- a/templates/cover-no-emblem.typ +++ /dev/null @@ -1,55 +0,0 @@ -#import "../functions/style.typ": * -#import "../functions/underline.typ": * -#import "../functions/hline.typ": * -#import "../info.typ": * -#import "../custom.typ": * - -#v(30pt) - -#set align(center + horizon) -#strong(论文中文题目) - -#v(2em) -#strong(论文英文题目) - - -#v(60pt) -#set text(字号.三号) - -#let fieldname(name) = [ - #set align(right + top) - #strong(name) - #h(0.25em) -] - -#let fieldvalue(value) = [ - #set align(center + horizon) - #set text(font: 字体.宋体) - #grid( - rows: (auto, auto), - row-gutter: 0.2em, - value, - line(length: 100%) - ) -] - -#grid( - columns: (80pt, 280pt), - row-gutter: 1em, - fieldname(text("姓") + h(2em) + text("名")), - fieldvalue(中文作者名), - fieldname(text("学") + h(2em) + text("号")), - fieldvalue(学号), - fieldname(text("学") + h(2em) + text("院")), - fieldvalue(学院), - fieldname(text("专") + h(2em) + text("业")), - fieldvalue(专业), - fieldname("研究方向"), - fieldvalue(方向), - fieldname("指导教师"), - fieldvalue(中文导师名), -) - -#v(2em) -#set text(字号.小二) -#text(日期) \ No newline at end of file diff --git a/templates/cover.typ b/templates/cover.typ deleted file mode 100644 index fac5ea7..0000000 --- a/templates/cover.typ +++ /dev/null @@ -1,74 +0,0 @@ -#import "../functions/style.typ": * -#import "../functions/underline.typ": * -#import "../functions/hline.typ": * -#import "../info.typ": * -#import "../custom.typ": * - -#box( - grid( - columns: (auto, auto), - gutter: 0.4em, - image("../images/vi/sysu-emblem.svg", height: 3em, fit: "contain") - ) -) -#linebreak() -#v(1em) -#text("本科生毕业论文(设计)", font: 字体.宋体, size: 字号.小初, weight: "semibold", fill: 强调色) -#hline(thickness: 3pt) -#v(-1em) -#hline(thickness: 1.2pt) - -#set text(字号.二号) -#v(60pt) -#grid( - columns: (80pt, 300pt), - [ - #set align(right + top) - 题目: - ], - [ - #set align(center + horizon) - #chineseunderline(论文中文题目, width: 300pt, bold: true) - ] -) - -#v(60pt) -#set text(字号.三号) - -#let fieldname(name) = [ - #set align(right + top) - #strong(name) - #h(0.25em) -] - -#let fieldvalue(value) = [ - #set align(center + horizon) - #set text(font: 字体.宋体) - #grid( - rows: (auto, auto), - row-gutter: 0.2em, - value, - line(length: 100%) - ) -] - -#grid( - columns: (80pt, 280pt), - row-gutter: 1em, - fieldname(text("姓") + h(2em) + text("名")), - fieldvalue(中文作者名), - fieldname(text("学") + h(2em) + text("号")), - fieldvalue(学号), - fieldname(text("学") + h(2em) + text("院")), - fieldvalue(学院), - fieldname(text("专") + h(2em) + text("业")), - fieldvalue(专业), - fieldname("研究方向"), - fieldvalue(方向), - fieldname("指导教师"), - fieldvalue(中文导师名), -) - -#v(2em) -#set text(字号.小二) -#text(日期) \ No newline at end of file diff --git a/templates/declaration.typ b/templates/declaration.typ deleted file mode 100644 index 76ca468..0000000 --- a/templates/declaration.typ +++ /dev/null @@ -1,20 +0,0 @@ -#import "../functions/style.typ": * -#import "../custom.typ": * - -#heading(numbering: none, outlined: false, "学术诚信声明") -#par(justify: true, first-line-indent: 2em, leading: 行距)[ - 本人郑重声明:所呈交的毕业论文(设计),是本人在导师的指导下,独立进行研究工作所取得的成果。除文中已经注明引用的内容外,本论文(设计)不包含任何其他个人或集体已经发表或撰写过的作品成果。对本论文(设计)的研究做出重要贡献的个人和集体,均已在文中以明确方式标明。本论文(设计)的知识产权归属于培养单位。本人完全意识到本声明的法律结果由本人承担。 -] - - -#v(3em) -#align(right + top, - box( - grid( - columns: (auto, auto), - gutter: 2em, - "作者签名:", "", - "日" + h(2em) + "期:", h(2.5em) + text("年") + h(2em) + text("月") + h(2em) + text("日") - ) - ) -) \ No newline at end of file diff --git a/thumbnail.png b/thumbnail.png new file mode 100644 index 0000000..aab4141 Binary files /dev/null and b/thumbnail.png differ diff --git a/typst.toml b/typst.toml new file mode 100644 index 0000000..b532b79 --- /dev/null +++ b/typst.toml @@ -0,0 +1,15 @@ +[package] +name = "morden-sysu-thesis" +version = "0.1.0" +entrypoint = "lib.typ" +repository = "https://gitlab.com/sysu-gitlab/thesis-template/better-thesis" +authors = ["howardlau1999 <@howardlau1999>", "Sunny Huang <@huangjj27>"] +license = "MIT" +description = "中山大学学位论文 Typst 模板,A Typst template for SYSU thesis" +keywords = ["SYSU", "thesis", "typst"] +categories = ["thesis"] + +[template] +path = "template" +entrypoint = "thesis.typ" +thumbnail = "thumbnail.png" diff --git a/utils/bilingual-bibliography.typ b/utils/bilingual-bibliography.typ new file mode 100644 index 0000000..e3049c0 --- /dev/null +++ b/utils/bilingual-bibliography.typ @@ -0,0 +1,131 @@ +// Authors: csimide, OrangeX4 +// Tested only on GB-7714-2015-Numeric +#let bilingual-bibliography( + bibliography: none, + title: "参考文献", + full: false, + style: "gb-7714-2015-numeric", + mapping: (:), +) = { + assert(bibliography != none, message: "请传入带有 source 的 bibliography 函数。") + + // Please fill in the remaining mapping table here + mapping = ( + //"等": "et al", + "卷": "Vol.", + "册": "Bk.", + // "译": "tran", + // "等译": "et al. tran", + // 注: 请见下方译者数量判断部分。 + ) + mapping + + let to-string(content) = { + if content.has("text") { + content.text + } else if content.has("children") { + content.children.map(to-string).join("") + } else if content.has("child") { + to-string(content.child) + } else if content.has("body") { + to-string(content.body) + } else if content == [ ] { + " " + } + } + + show grid.cell.where(x: 1): it => { + // 后续的操作是对 string 进行的。 + let ittext = to-string(it) + // 判断是否为中文文献:去除特定词组后,仍有至少两个连续汉字。 + let pureittext = ittext.replace(regex("[等卷册和版本章期页篇译间者(不详)]"), "") + if pureittext.find(regex("\p{sc=Hani}{2,}")) != none { + ittext + } else { + // 若不是中文文献,进行替换 + // 第xxx卷、第xxx册的情况:变为 Vol. XXX 或 Bk. XXX。 + let reptext = ittext + reptext = reptext.replace( + regex("(第\s?)?\d+\s?[卷册]"), + itt => { + if itt.text.contains("卷") { + "Vol. " + } else { + "Bk. " + } + itt.text.find(regex("\d+")) + }, + ) + + // 第xxx版/第xxx本的情况:变为 1st ed 格式。 + reptext = reptext.replace( + regex("(第\s?)?\d+\s?[版本]"), + itt => { + let num = itt.text.find(regex("\d+")) + num + if num.clusters().len() == 2 and num.clusters().first() == "1" { + "th" + } else { + ( + "1": "st", + "2": "nd", + "3": "rd", + ).at(num.clusters().last(), default: "th") + } + " ed" + }, + ) + + // 译者数量判断:单数时需要用 trans,复数时需要用 tran 。 + // 注: + // 1. 目前判断译者数量的方法非常草率,有逗号就是多个作者。但是在部分 GB/T 7714-2015 方言中,姓名中可以含有逗号。 + // 2. 在 GB/T 7714-2015 原文中有 `等译`(P15 10.1.3 小节 示例 1-[1]),但未给出相应的英文缩写翻译。 + // 3. CSL 社区库内的 GB/T 7714-2015 会使用 `等, 译` 和 `et al., tran` 的写法。为了保证统一性,这里会译作不带逗号的版本,即 `et al. tran` + // 如果工作不正常,可以考虑换为简单关键词替换,即注释这段情况,取消 13 行 mapping 内 `译` 条目的注释。 + reptext = reptext.replace( + regex("\].+?译"), + itt => { + // 我想让上面这一行匹配变成非贪婪的,但加问号后没啥效果? + if itt.text.replace(regex(",\s?译"), "").find(",") != none { + itt.text.replace(regex(",?\s?译"), " tran") + } else { + itt.text.replace(regex(",?\s?译"), " trans") + } + }, + ) + + // `等` 特殊处理:`等`后方接内容也需要译作 `et al.`,如 `等译` 需要翻译为 `et al. trans` + reptext = reptext.replace( + regex("等."), + itt => { + "et al." + // 如果原文就是 `等.`,则仅需简单替换,不需要额外处理 + // 如果原文 `等` 后没有跟随英文标点,则需要补充一个空格 + if not itt.text.last() in (".", ",", ";", ":", "[", "]", "/", "\\", "<", ">", "?", "(", ")", " ", "\"", "'") { + " " + } + // 原文有英文句号时不需要重复句号,否则需要将匹配到的最后一个字符吐回来 + if not itt.text.last() == "." { + itt.text.last() + } + }, + ) + + // 其他情况:直接替换 + reptext = reptext.replace( + regex("\p{sc=Hani}+"), + itt => { + mapping.at(itt.text, default: itt.text) + // 注意:若替换功能工作良好,应该不会出现 `default` 情形 + }, + ) + reptext + } + } + + set text(lang: "zh") + bibliography( + title: title, + full: full, + style: style, + ) +} diff --git a/utils/custom-heading.typ b/utils/custom-heading.typ new file mode 100644 index 0000000..02eb87f --- /dev/null +++ b/utils/custom-heading.typ @@ -0,0 +1,50 @@ +// 展示一个标题 +#let heading-display(it) = { + if it != none { + if it.has("numbering") and it.numbering != none { + numbering(it.numbering, ..counter(heading).at(it.location())) + [ ] + } + it.body + } else { + "" + } +} + +// 获取当前激活的 heading,参数 prev 用于标志优先使用之前页面的 heading +#let active-heading(level: 1, prev: true, loc) = { + // 之前页面的标题 + let prev-headings = query(selector(heading.where(level: level)).before(loc), loc) + // 当前页面的标题 + let cur-headings = query(selector(heading.where(level: level)).after(loc), loc) + .filter(it => it.location().page() == loc.page()) + if prev-headings.len() == 0 and cur-headings.len() == 0 { + return none + } else { + if prev { + if prev-headings.len() != 0 { + return prev-headings.last() + } else { + return cur-headings.first() + } + } else { + if cur-headings.len() != 0 { + return cur-headings.first() + } else { + return prev-headings.last() + } + } + } +} + +// 获取当前页面的标题 +#let current-heading(level: 1, loc) = { + // 当前页面的标题 + let cur-headings = query(selector(heading.where(level: level)).after(loc), loc) + .filter(it => it.location().page() == loc.page()) + if cur-headings.len() != 0 { + return cur-headings.first() + } else { + return none + } +} \ No newline at end of file diff --git a/utils/custom-numbering.typ b/utils/custom-numbering.typ new file mode 100644 index 0000000..a5c8847 --- /dev/null +++ b/utils/custom-numbering.typ @@ -0,0 +1,30 @@ +// 一个简单的自定义 Numbering +// 用法也简单,可以特殊设置一级等标题的样式,以及一个缺省值 +#let custom-numbering(base: 1, depth: 5, first-level: auto, second-level: auto, third-level: auto, format, ..args) = { + if (args.pos().len() > depth) { + return + } + if (first-level != auto and args.pos().len() == 1) { + if (first-level != "") { + numbering(first-level, ..args) + } + return + } + if (second-level != auto and args.pos().len() == 2) { + if (second-level != "") { + numbering(second-level, ..args) + } + return + } + if (third-level != auto and args.pos().len() == 3) { + if (third-level != "") { + numbering(third-level, ..args) + } + return + } + // default + if (args.pos().len() >= base) { + numbering(format, ..(args.pos().slice(base - 1))) + return + } +} \ No newline at end of file diff --git a/utils/custom-tablex.typ b/utils/custom-tablex.typ new file mode 100644 index 0000000..29fe03b --- /dev/null +++ b/utils/custom-tablex.typ @@ -0,0 +1 @@ +#import "@preview/tablex:0.0.8": * \ No newline at end of file diff --git a/utils/datetime-display.typ b/utils/datetime-display.typ new file mode 100644 index 0000000..2012033 --- /dev/null +++ b/utils/datetime-display.typ @@ -0,0 +1,9 @@ +// 显示中文日期 +#let datetime-display(date) = { + date.display("[year] 年 [month] 月 [day] 日") +} + +// 显示英文日期 +#let datetime-en-display(date) = { + date.display("[month repr:short] [day], [year]") +} \ No newline at end of file diff --git a/utils/double-underline.typ b/utils/double-underline.typ new file mode 100644 index 0000000..74e1dd5 --- /dev/null +++ b/utils/double-underline.typ @@ -0,0 +1,11 @@ +// 双下划线 +#let double-underline(body) = style(styles => { + let size = measure(body, styles) + stack( + body, + v(3pt), + line(length: size.width), + v(2pt), + line(length: size.width), + ) +}) \ No newline at end of file diff --git a/utils/hline.typ b/utils/hline.typ new file mode 100644 index 0000000..afd563c --- /dev/null +++ b/utils/hline.typ @@ -0,0 +1,4 @@ +// 一条水平横线 +#let hline() = { + line(length: 100%) +} \ No newline at end of file diff --git a/utils/indent.typ b/utils/indent.typ new file mode 100644 index 0000000..427bc09 --- /dev/null +++ b/utils/indent.typ @@ -0,0 +1,6 @@ +// 中文缩进 +#let indent = h(2em) + +// 假段落,附着于 heading 之后可以实现首行缩进 +#let empty-par = par[#box()] +#let fake-par = context empty-par + v(-measure(empty-par + empty-par).height) \ No newline at end of file diff --git a/utils/invisible-heading.typ b/utils/invisible-heading.typ new file mode 100644 index 0000000..f16fa80 --- /dev/null +++ b/utils/invisible-heading.typ @@ -0,0 +1,5 @@ +// 用于创建一个不可见的标题,用于给 outline 加上短标题 +#let invisible-heading(..args) = { + set text(size: 0pt, fill: white) + heading(numbering: none, ..args) +} \ No newline at end of file diff --git a/utils/justify-text.typ b/utils/justify-text.typ new file mode 100644 index 0000000..318027d --- /dev/null +++ b/utils/justify-text.typ @@ -0,0 +1,9 @@ +// 双端对其一段小文本,常用于表格的中文 key +#let justify-text(with-tail: false, tail: ":", body) = { + if with-tail and tail != "" { + stack(dir: ltr, stack(dir: ltr, spacing: 1fr, ..body.split("").filter(it => it != "")), tail) + } else { + stack(dir: ltr, spacing: 1fr, ..body.split("").filter(it => it != "")) + } +} + diff --git a/utils/style.typ b/utils/style.typ new file mode 100644 index 0000000..294df91 --- /dev/null +++ b/utils/style.typ @@ -0,0 +1,43 @@ +#let 字号 = ( + 初号: 42pt, + 小初: 36pt, + 一号: 26pt, + 小一: 24pt, + 二号: 22pt, + 小二: 18pt, + 三号: 16pt, + 小三: 15pt, + 四号: 14pt, + 中四: 13pt, + 小四: 12pt, + 五号: 10.5pt, + 小五: 9pt, + 六号: 7.5pt, + 小六: 6.5pt, + 七号: 5.5pt, + 小七: 5pt, +) + +#let 字体 = ( + // 宋体,属于「有衬线字体」,一般可以等同于英文中的 Serif Font + // 这一行分别是「新罗马体(有衬线英文字体)」、「思源宋体(简体)」、「思源宋体」、「宋体(Windows)」、「宋体(MacOS)」 + 宋体: ("Times New Roman", "Source Han Serif SC", "Source Han Serif", "Noto Serif CJK SC", "SimSun", "Songti SC", "STSongti"), + // 黑体,属于「无衬线字体」,一般可以等同于英文中的 Sans Serif Font + // 这一行分别是「Arial(无衬线英文字体)」、「思源黑体(简体)」、「思源黑体」、「黑体(Windows)」、「黑体(MacOS)」 + 黑体: ("Arial", "Source Han Sans HW SC", "Source Han Sans", "Noto Sans CJK SC", "SimHei", "Heiti SC", "STHeiti"), + // 楷体 + 楷体: ("Times New Roman", "KaiTi", "Kaiti SC", "STKaiti", "FZKai-Z03S", "Noto Serif CJK SC"), + // 仿宋 + 仿宋: ("Times New Roman", "FangSong", "FangSong SC", "STFangSong", "FZFangSong-Z02S", "Noto Serif CJK SC"), + // 等宽字体,用于代码块环境,一般可以等同于英文中的 Monospaced Font + // 这一行分别是「Courier New(Windows 等宽英文字体)」、「思源等宽黑体(简体)」、「思源等宽黑体」、「黑体(Windows)」、「黑体(MacOS)」 + 等宽: ("Courier New", "Menlo", "IBM Plex Mono", "Source Han Sans HW SC", "Source Han Sans HW", "Noto Sans Mono CJK SC", "SimHei", "Heiti SC", "STHeiti"), +) + +// 定义中山大学规范颜色 +// 参考 http://home3.sysu.edu.cn/sysuvi/content/zaisheng/b/C-1-5.jpg +#let sysucolor = ( + green: cmyk(100%, 0%, 100%, 60%), + red: cmyk(30%, 100%, 100%, 50%), + black: cmyk(0%, 0%, 0%, 100%), +) diff --git a/utils/unpairs.typ b/utils/unpairs.typ new file mode 100644 index 0000000..6931e95 --- /dev/null +++ b/utils/unpairs.typ @@ -0,0 +1,8 @@ +// 将 pairs 数组转成 dict 字典 +#let unpairs(pairs) = { + let dict = (:) + for pair in pairs { + dict.insert(..pair) + } + dict +} \ No newline at end of file