├── .gitignore ├── images └── sysu.png ├── functions ├── fonts.typ ├── helpers.typ └── booktab.typ ├── templates ├── homework.typ ├── report.typ └── shared.typ ├── LICENSE ├── README.md └── usage.typ /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.pdf 2 | images/** 3 | !images/sysu.png 4 | /*.typ 5 | !/usage.typ 6 | -------------------------------------------------------------------------------- /images/sysu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GZTimeWalker/GZ-Typst-Templates/HEAD/images/sysu.png -------------------------------------------------------------------------------- /functions/fonts.typ: -------------------------------------------------------------------------------- 1 | #let line_height = 1em 2 | 3 | #let fonts = ( 4 | serif: ("Source Han Serif SC", "Source Han Serif"), 5 | sans: ("Source Han Sans SC", "Source Han Sans"), 6 | monospace: ("JetBrains Mono", "Consolas"), 7 | ) 8 | 9 | #let textbf(it) = block(text(font: fonts.sans, weight: "semibold", it)) 10 | 11 | #let textit(it) = block(text(style: "italic", it)) 12 | -------------------------------------------------------------------------------- /functions/helpers.typ: -------------------------------------------------------------------------------- 1 | #let zip(..lists) = { 2 | let lists = lists.pos() 3 | if lists == () { 4 | () 5 | } else { 6 | let ret = () 7 | let len = lists.fold(lists.first().len(), (a, b) => if a > b.len() { b.len() } else { a }) 8 | 9 | for i in range(0, len) { 10 | let curr = () 11 | for list in lists { 12 | curr.push(list.at(i)) 13 | } 14 | ret.push(curr) 15 | } 16 | 17 | ret 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /templates/homework.typ: -------------------------------------------------------------------------------- 1 | #import "shared.typ": * 2 | 3 | #let report(subject: "课程", title: "作业一", name: "张三", stdid: "11223344", body) = { 4 | set document(title: title) 5 | show: shared 6 | 7 | let fieldname(name) = [ 8 | #set align(right + horizon) 9 | #set text(font: fonts.serif) 10 | #name 11 | ] 12 | 13 | let cell = rect.with(width: 100%, radius: 6pt, stroke: none) 14 | 15 | let fieldvalue(value) = [ 16 | #set align(left + horizon) 17 | #set text(font: fonts.serif, weight: "medium", size: 13pt) 18 | #cell(value) 19 | ] 20 | 21 | set page(header: align(center)[ 22 | #grid( 23 | columns: (auto, auto, auto, auto), 24 | gutter: 1em, 25 | fieldvalue(subject), 26 | fieldvalue(title), 27 | fieldvalue(name), 28 | fieldvalue(stdid), 29 | ) 30 | ]) 31 | 32 | show par: set block(spacing: line_height) 33 | set align(left + top) 34 | set par(justify: true, first-line-indent: 0pt, leading: line_height) 35 | 36 | body 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 GZTime 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 | -------------------------------------------------------------------------------- /functions/booktab.typ: -------------------------------------------------------------------------------- 1 | #import "helpers.typ": * 2 | #import "fonts.typ": * 3 | 4 | #let booktab(columns: (), aligns: (), width: auto, caption: none, ..cells) = { 5 | let headers = cells.pos().slice(0, columns.len()) 6 | let contents = cells.pos().slice(columns.len(), cells.pos().len()) 7 | set align(center) 8 | set text(size: 0.95em) 9 | 10 | if aligns == () { 11 | for i in range(0, columns.len()) { 12 | aligns.push(center) 13 | } 14 | } 15 | 16 | let content_aligns = () 17 | for i in range(0, contents.len()) { 18 | content_aligns.push(aligns.at(calc.rem(i, aligns.len()))) 19 | } 20 | 21 | figure(block( 22 | width: width, 23 | grid(columns: (auto), row-gutter: 0.6em, line(length: 100%), [ 24 | #set align(center) 25 | #box( 26 | width: 100% - 1em, 27 | grid(columns: columns, ..zip(headers, aligns).map(it => [ 28 | #set align(it.last()) 29 | #textbf(it.first()) 30 | ])), 31 | ) 32 | ], line(length: 100%), [ 33 | #set align(center) 34 | #box(width: 100% - 1em, grid( 35 | columns: columns, 36 | row-gutter: 1em, 37 | ..zip(contents, content_aligns).map(it => [ 38 | #set align(it.last()) 39 | #it.first() 40 | ]), 41 | )) 42 | ], line(length: 100%)), 43 | ), caption: caption, kind: table) 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GZ Typst Templates 2 | 3 | For my personal usage. 4 | 5 | 个人使用的 Typst 模板,用于写作业和实验报告。 6 | 7 | ## Fonts 8 | 9 | Install the fonts by yourself. 10 | 11 | 你可能需要自己安装一些字体,当然,这只是个人习惯( 12 | 13 | - text: ("CMU Serif", "Source Han Serif SC"), 14 | - sans: ("Source Han Sans SC"), 15 | - code: ("JetBrains Mono", "Consolas"), 16 | 17 | ## Usage 18 | 19 | - Install [Typst](https://typst.app) 20 | - Clone this repo to any directory 21 | - Link the repo to your project by `ln -s /path/to/repo/ ./base` 22 | - Use this template in Typst (e.g. `#import "./base/templates/report.typ": *`) 23 | 24 | - 安装 [Typst](https://typst.app) 25 | - 将本仓库克隆到任意目录 26 | - 通过 `ln -s /path/to/repo/ ./base` 将本仓库链接到你的项目 27 | - 在 Typst 中使用模板(例如 `#import "./base/templates/report.typ": *`) 28 | 29 | You can view the example in [usage.typ](./usage.typ). 30 | 31 | 你可以在 [usage.typ](./usage.typ) 中查看示例。 32 | 33 | Suggestions: use relative path for the link, because Typst uses the directory of the compiled file as the root directory by default. 34 | 35 | 建议使用链接,因为 Typst 默认以编译文件所在目录为根目录。 36 | 37 | ## Templates 38 | 39 | ### Report 40 | 41 | Use for writing lab reports. 42 | 43 | 用来写实验报告的模板,有封面。 44 | 45 | ```typ 46 | #import "./base/templates/report.typ": * 47 | 48 | #show: report.with( 49 | title: "实验报告实验", 50 | subtitle: "尝试使用 Typst 编写实验报告", 51 | name: "张三", 52 | stdid: "22223333", 53 | classid: "实验二班", 54 | major: "实验报告编写技术", 55 | school: "实验报告学院", 56 | time: "2022~2023 学年第二学期", 57 | banner: "../images/sysu.png" // 图片路径,相对于 report.typ 所在目录 58 | ) 59 | ``` 60 | 61 | ### Homework 62 | 63 | Use for writing homework. 64 | 65 | 这个模板用来写作业(数学作业),就是每一页最上面会有一些信息。 66 | 67 | ```typ 68 | #import "./base/templates/homework.typ": * 69 | 70 | #show: report.with( 71 | subject: "量子计算", 72 | title: "作业二", 73 | name: "张三", 74 | stdid: "22223333", 75 | ) 76 | ``` 77 | -------------------------------------------------------------------------------- /usage.typ: -------------------------------------------------------------------------------- 1 | #import "templates/report.typ": * 2 | 3 | #show: report.with( 4 | title: "实验报告实验", 5 | subtitle: "尝试使用 Typst 编写实验报告", 6 | name: "张三", 7 | stdid: "22223333", 8 | classid: "实验二班", 9 | major: "实验报告编写技术", 10 | school: "实验报告学院", 11 | time: "2022~2023 学年第二学期", 12 | banner: "../images/sysu.png" 13 | ) 14 | 15 | // write your report here 16 | 17 | #title(content: "尝试使用 Typst 编写实验报告") 18 | 19 | = 实验报告实验 20 | 21 | == 什么是实验报告 22 | 23 | #lorem(200) 24 | 25 | #booktab( 26 | width: 100%, 27 | aligns: (left + horizon, center, right + bottom), 28 | columns: (1fr, 1fr, 1fr), 29 | caption: [`booktab` 示例], 30 | [左对齐], [居中], [右对齐], 31 | [4], [5], [6], 32 | [7], [8], [9], 33 | [10], [$ (n(n+1)) / 2 $], [11], 34 | ) 35 | 36 | === 公式 37 | 38 | $ sum_(k=0)^n k 39 | &= 1 + ... + n \ 40 | &= (n(n+1)) / 2 $ 41 | 42 | == 什么是 Typst 43 | 44 | #lorem(20) 45 | 46 | 行内代码块: `#lorem(20)` 47 | 48 | 49 | 50 | ```c 51 | int main() { 52 | // 这是一句中文注释 53 | // This is a comment in English 54 | printf("Hello, world!"); 55 | return 0; 56 | } 57 | ``` 58 | 59 | == 做个数学题 60 | 61 | #set par(first-line-indent: 0pt) 62 | 63 | 3.1 设 $cal(V)$ 和 $cal(W)$ 是两 Hilbert 空间,${ket(v_j)}$ 和 ${ket(w_j)}$ 分别为 $cal(V)$ 和 $cal(W)$ 的一组正交模基。设 $display(T_A: sum_(j = 1)^n c_j ket(v_j) arrow.r sum_(j=1)^n c_j sum_(i=1)^m a_(i j) ket(w_j))$,证明 $T_A$ 是线性算子。 64 | 65 | 证明:对于 $forall k in NN, 0 < k <= n$,可以取一组 $display(c_j = cases( 66 | 1 "," j = k, 67 | 0 "," j != k 68 | ))$,由上述定义,可得到: 69 | 70 | $ T_A (sum_(j = 1)^n c_j ket(v_j)) &= T_A (c_k ket(v_j)) = T_A (ket(v_j)) \ 71 | &= sum_(j=1)^n c_j sum_(i=1)^m a_(i j) ket(w_j) = c_k sum_(i=1)^m a_(i j) ket(w_j) = sum_(i=1)^m a_(i j) ket(w_j) $ 72 | 73 | 则对于任意的一组 $c_j$,可以得到: 74 | 75 | $ T_A (sum_(j = 1)^n c_j ket(v_j)) = sum_(j=1)^n c_j sum_(i=1)^m a_(i j) ket(w_j) = sum_(j=1)^n c_j T_A (ket(v_j)) $ 76 | 77 | 即 $T_A$ 是线性算子。 78 | -------------------------------------------------------------------------------- /templates/report.typ: -------------------------------------------------------------------------------- 1 | #import "shared.typ": * 2 | 3 | #let report( 4 | title: "实验报告", 5 | subtitle: "Lab 0: 环境准备", 6 | name: "张三", 7 | stdid: "11223344", 8 | classid: "计科一班", 9 | major: "计算机科学与技术", 10 | school: "计算机学院", 11 | time: "2022~2023 学年第二学期", 12 | banner: none + "", 13 | body, 14 | ) = { 15 | set document(title: title) 16 | 17 | show: shared 18 | 19 | let fieldname(name) = [ 20 | #set align(right + horizon) 21 | #set text(font: fonts.serif) 22 | #name 23 | ] 24 | 25 | let fieldvalue(value) = [ 26 | #set align(left + horizon) 27 | #set text(font: fonts.serif) 28 | #value 29 | ] 30 | 31 | align(center)[ 32 | #if (banner == none) { 33 | v(200pt) 34 | } else { 35 | image(banner, height: 200pt, width: 95%, fit: "contain") 36 | } 37 | #linebreak() 38 | #v(50pt, weak: true) 39 | #block(text(weight: "semibold", 30pt, title)) 40 | #v(50pt, weak: true) 41 | #block(text(weight: "medium", 24pt, subtitle)) 42 | 43 | #v(140pt, weak: true) 44 | #set text(14.5pt) 45 | 46 | #grid( 47 | columns: (200pt, 240pt), 48 | row-gutter: 1em, 49 | fieldname(text("姓") + h(2em) + text("名:") + h(1em)), 50 | fieldvalue(name), 51 | fieldname(text("学") + h(2em) + text("号:") + h(1em)), 52 | fieldvalue(stdid), 53 | fieldname(text("教学班号:") + h(1em)), 54 | fieldvalue(classid), 55 | fieldname(text("专") + h(2em) + text("业:") + h(1em)), 56 | fieldvalue(major), 57 | fieldname(text("院") + h(2em) + text("系:") + h(1em)), 58 | fieldvalue(school), 59 | ) 60 | 61 | #v(40pt) 62 | #set text(13pt) 63 | #text(time) 64 | #pagebreak() 65 | ] 66 | 67 | set align(left + top) 68 | set par(justify: true, first-line-indent: 2em, leading: line_height) 69 | 70 | body 71 | } 72 | 73 | #let title(content: "") = align(center)[ 74 | #set text(26pt, weight: "bold") 75 | #v(2em) 76 | #content 77 | #v(1.625em) 78 | ] 79 | -------------------------------------------------------------------------------- /templates/shared.typ: -------------------------------------------------------------------------------- 1 | #import "../functions/fonts.typ": * 2 | #import "../functions/booktab.typ": * 3 | #import "@preview/physica:0.8.0": * 4 | 5 | // Set the default style for the document 6 | #let shared(doc) = { 7 | set page(paper: "a4", numbering: "1", number-align: center) 8 | set text(font: fonts.serif, lang: "zh", size: 11pt) 9 | 10 | show raw.where(block: true): block.with(fill: luma(240), inset: (x: 1.25em, y: 1em), width: 100%, radius: 4pt) 11 | 12 | show raw.where(block: true): par.with(first-line-indent: 0em, justify: true, leading: 8pt) 13 | 14 | show raw.where(block: false): box.with( 15 | fill: luma(240), 16 | inset: (x: 5pt, y: 0pt), 17 | outset: (y: 4pt), 18 | radius: 3pt, 19 | ) 20 | 21 | show raw: text.with(font: fonts.monospace + fonts.sans, size: 1em) 22 | 23 | set heading(numbering: (..numbers) => { 24 | let level = numbers.pos().len(); 25 | 26 | if (level == 1) { 27 | return numbering("一. ", numbers.pos().at(level - 1)) 28 | } else { 29 | return numbering("1.1. ", ..numbers.pos().slice(1, level)) 30 | } 31 | }) 32 | 33 | show heading: it => [ 34 | // Cancel indentation for headings of level 2 or above 35 | #set par(first-line-indent: 0em, hanging-indent: 2em) 36 | 37 | #let sized_heading(it, size, weight, mt, mb) = { 38 | set text(size, weight: weight) 39 | v(mt) 40 | counter(heading).display() 41 | text[#it.body] 42 | v(mb) 43 | } 44 | 45 | #if it.level == 1 { 46 | sized_heading(it, 20pt, "semibold", 1em, 0.6em) 47 | } else if it.level == 2 { 48 | sized_heading(it, 16pt, "semibold", 0.7em, 0.7em) 49 | } else if it.level == 3 { 50 | sized_heading(it, 14pt, "medium", 0.5em, 0.5em) 51 | } else { 52 | sized_heading(it, 12pt, "medium", 0.3em, 0.3em) 53 | } 54 | ] 55 | 56 | show link: underline 57 | set math.vec(delim: "[") 58 | set math.mat(delim: "[") 59 | set par(spacing: line_height) 60 | 61 | doc 62 | } 63 | --------------------------------------------------------------------------------