From 0c743e342a52abfe446614139379f3a3ce0d2eee Mon Sep 17 00:00:00 2001 From: Kevin Sun Date: Thu, 31 Oct 2024 16:37:18 +0800 Subject: [PATCH] import lovelace --- example.typ | 37 ++++++--- .../layouts/mainmatter.typ | 68 +++------------- sustech-graduated-thesis/lib.typ | 80 ++++++++++++++----- .../pages/abstract-en.typ | 4 +- sustech-graduated-thesis/pages/abstract.typ | 3 +- .../pages/final-cover.typ | 28 ++----- .../pages/nonfinal-cover.typ | 11 +-- sustech-graduated-thesis/utils/eq-wrap.typ | 58 ++++++++++++++ 8 files changed, 171 insertions(+), 118 deletions(-) create mode 100644 sustech-graduated-thesis/utils/eq-wrap.typ diff --git a/example.typ b/example.typ index 3a22353..c7f9156 100644 --- a/example.typ +++ b/example.typ @@ -1,7 +1,6 @@ -#import "sustech-graduated-thesis/lib.typ": documentclass, indent, notation, fake-par, 字体 +#import "sustech-graduated-thesis/lib.typ": documentclass, indent, notation, fake-par, 字体, pseudocode, pseudocode-list #import "sustech-graduated-thesis/utils/math-utils.typ": sfrac, svec - // 参考 modern-nju-thesis: // 你首先应该安装 https://github.com/nju-lug/modern-nju-thesis/tree/main/fonts/FangZheng 里的所有字体, // 如果是 Web App 上编辑,你应该手动上传这些字体文件,否则不能正常使用「楷体」和「仿宋」,导致显示错误。 @@ -14,6 +13,7 @@ outline-page, list-of-figures, list-of-tables, notation-page, acknowledgement, ) = documentclass( doctype: "midterm", // proposal, midterm, final + degree: "MEng", // 参考`degree-names.typ` // anonymous: true, // 盲审模式 twoside: true, // 双面模式,会加入空白页,便于打印 // fonts: (楷体: ("Times New Roman", "FZKai-Z03S")), // 可自定义字体,先英文字体后中文字体,应传入「宋体」、「黑体」、「楷体」、「仿宋」、「等宽」 @@ -23,7 +23,7 @@ // math-breakable: false, // 多行公式可否分割到多页 // sep-ref: true, // 是否自动将@ref与其跟随的中文字符分开处理,使用true时应避免含有中文的label或bib info: ( - title: ("基于Typst的", "南方科技大学学位论文"), + title: ("基于Typst的南方科技大学学位论文样例代码"), title-en: "SUSTech Thesis Template for Typst", grade: "20XX", student-id: "1234567890", @@ -64,14 +64,14 @@ #set enum(numbering: "(1 a)", indent: 0em) #show: mainmatter -// 中文摘要 +// 中文摘要,非最终报告会被隐藏 #abstract( keywords: ("我", "就是", "测试用", "关键词") )[ 中文摘要 ] -// 英文摘要 +// 英文摘要,非最终报告会被隐藏 #abstract-en( keywords: ("Dummy", "Keywords", "Here", "It Is") )[ @@ -81,10 +81,10 @@ // 目录 #outline-page() -// 插图目录 +// 插图目录,非最终报告会被隐藏 #list-of-figures() -// 表格目录 +// 表格目录,非最终报告会被隐藏 #list-of-tables() // 符号表 @@ -212,11 +212,30 @@ $ <-> caption:[代码块], ) +如果需要使用伪代码,本模板已经引入了`lovelace`库中的函数,如@fig:pseudo所示。 + +#figure( + kind: "algorithm", + + pseudocode-list(booktabs: true, numbered-title: [样例算法])[ + + *输入*:$alpha$—样例输入1的解释;$beta$—样例输入2的解释 + + *输出*:$p$—样例输出1的解释;$q$—样例输出2的解释 + + *while* 未达到指定要求 + + $p arrow.l$循环计算$alpha$…… + + *if* $p > 1$ *then* + + $q arrow.l$条件计算$beta$…… + + *else* + + *break* + + *end* + + *end* + ] +) + == 注意事项 - 为了避免不必要的空格,中文内部(包括标点符号)不能换行。 否则就像本行,在第一个句号后加入了额外的空格。 -- 为了使得标题后首行文字可以缩进,本模板使用了`#fake-par`,会导致标题行和下一行可能不同页,如@sec:bib,建议使用手动换行“`\`”解决。 +- 为了使得标题后首行文字可以缩进,本模板使用了`#fake-par`,会导致标题行和下一行可能不同页,建议使用手动换行“`\`”解决。 - 另外,对于行间公式和有序/无序列表之后马上需要另起一段的,需使用`#fake-par`另起一段,仅使用空行无法另起一段。 - 如需设置全局格式,请在`#show: mainmatter`之前设置,或在设置之后再次应用`#show: mainmatter`,以免模板的某些全局设置失效。 #fake-par @@ -297,7 +316,7 @@ $ // 默认使用 gb-7714-2015-numeric 样式 #bilingual-bibliography(full: true) -// 致谢 +// 致谢,非最终报告会被隐藏 #acknowledgement[ 感谢modern-nju-thesis模板,感谢SUSTech LaTeX模板。 ] diff --git a/sustech-graduated-thesis/layouts/mainmatter.typ b/sustech-graduated-thesis/layouts/mainmatter.typ index c7ec98b..2c353d1 100644 --- a/sustech-graduated-thesis/layouts/mainmatter.typ +++ b/sustech-graduated-thesis/layouts/mainmatter.typ @@ -6,8 +6,8 @@ #import "../utils/custom-heading.typ": heading-display, active-heading, current-heading #import "../utils/indent.typ": fake-par #import "../utils/unpairs.typ": unpairs +#import "../utils/eq-wrap.typ": eq-wrap, arounds_default -#let arounds_default = "、,。!?…;:—‘’“”()【】《》~!@#$%^&*()-_=+/*[]{}|\\:;'\"<>,./?  " #let mainmatter( // documentclass 传入参数 @@ -111,7 +111,7 @@ // 3.2 脚注样式 show footnote.entry: set text(font: fonts.宋体, size: 字号.五号) // 3.3 设置 figure 的编号 - show heading: reset-counters + show heading: reset-counters.with(extra-kinds: ("algorithm",)) show figure: show-figure // 3.4 设置 equation 的编号和假段落首行缩进 show math.equation: set text(font: (math-font, ) + fonts.宋体) @@ -146,6 +146,12 @@ show figure.where( kind: raw ): set block(above: 1em, below: 1.5em) + show figure.where( + kind: "algorithm" + ): set figure(supplement: "算法") + show figure.where( + kind: "algorithm" + ): set block(above: 1em, below: 1.5em) set figure.caption(separator: separator) show figure.caption: set text(font: fonts.宋体, size: caption-size) show table: set text(size: caption-size) @@ -245,63 +251,7 @@ ) ]) - // 3.5 inline 公式两边的空格,必须放在最后 - let eq-wrap(cont) = { - if not cont.has("children") { - return cont - } - let cont-fn = cont.func() - let cont-fs = cont.fields() - let _ = cont-fs.remove("children") - - let map-fn = ((n, elem)) => { - if elem.func() in (list.item, enum.item, figure, table, table.cell) { - let fs = elem.fields() - let _ = fs.remove("body") - let lab = if "label" in fs {fs.remove("label")} - let _ = if "caption" in fs {fs.insert("caption", eq-wrap(elem.caption.body))} - let fn = elem.func() - let wrapped = eq-wrap(elem.body) - if lab != none and elem.func() in (list.item, enum.item, figure) { - [#fn(wrapped, ..fs)#lab] - } else { - fn(wrapped, ..fs) - } - } else { - if (elem.func() == math.equation and elem.block == false) or repr(elem) == "context()" { - if n > 0 { - if cont.children.at(n - 1).has("text") { - let prev = cont.children.at(n - 1).text.last() - if not arounds.contains(prev) { - [ ] - } - } - } - elem - if n < cont.children.len() - 1 { - if cont.children.at(n + 1).has("text") { - let next = cont.children.at(n + 1).text.first() - if not arounds.contains(next) { - [ ] - } - } - } - } else { - elem - } - } - } - - if repr(cont-fn) == "sequence" { - for (n, elem) in cont.children.enumerate() { - map-fn((n, elem)) - } - } else { - let new-child = cont.children.enumerate().map(map-fn) - cont-fn(..new-child, ..cont-fs) - } - } - show: eq-wrap + show: eq-wrap.with(arounds: arounds) it } \ No newline at end of file diff --git a/sustech-graduated-thesis/lib.typ b/sustech-graduated-thesis/lib.typ index fe96a9b..bd6d91c 100644 --- a/sustech-graduated-thesis/lib.typ +++ b/sustech-graduated-thesis/lib.typ @@ -21,6 +21,7 @@ #import "utils/style.typ": 字体, 字号 #import "utils/state-notations.typ": notation, notations #import "utils/multi-line-equate.typ": show-figure, equate, equate-ref +#import "utils/eq-wrap.typ": eq-wrap #import "@preview/unify:0.6.0": num as _num, qty as _qty, numrange as _numrange, qtyrange as _qtyrange #let num(value) = _num(value, multiplier: "×", thousandsep: ",") @@ -28,9 +29,29 @@ #let qty(value, unit, rawunit: false) = _qty(value, unit, rawunit: rawunit, multiplier: "×", thousandsep: ",") #let qtyrange(lower, upper, unit, rawunit: false) = _qtyrange(lower, upper, unit, rawunit: rawunit, multiplier: "×", thousandsep: ",") +#import "@preview/lovelace:0.3.0": pseudocode as _pseudocode, pseudocode-list as _pseudocode-list +#let pseudocode(line-numbering: "1", + line-number-supplement: "Line", + stroke: 0.5pt + gray, + indentation: 1em, + hooks: 0pt, + line-gap: .8em, + booktabs-stroke: black + 1pt, + booktabs: false, + title: none, + numbered-title: none, + ..children) = { + _pseudocode(line-numbering, line-number-supplement, stroke, indentation, hooks, line-gap, booktabs-stroke, booktabs, title, numbered-title, ..children.map(x => eq-warp(x))) + } +#let pseudocode-list(..config, body) = { + let transformed-body = eq-wrap(body) + _pseudocode-list(..config, transformed-body) + } + // 使用函数闭包特性,通过 `documentclass` 函数类进行全局信息配置,然后暴露出拥有了全局配置的、具体的 `layouts` 和 `templates` 内部函数。 #let documentclass( doctype: "final", // "proposal" | "midterm" | "final",文章类型,默认为最终报告 final + degree: "MEng", // 参考`degree-names.typ` twoside: true, // 双面模式,会加入空白页,便于打印 anonymous: false, // 盲审模式 bibliography: none, // 原来的参考文献函数 @@ -74,12 +95,12 @@ supervisor-contact: "南方科技大学 广东省深圳市南山区学苑大道1088号", email: "xyz@mail.sustech.edu.cn", school-code: "518055", - degree: "MEng", ) + info ( // 将传入参数再导出 doctype: doctype, + degree: degree, twoside: twoside, anonymous: anonymous, fonts: fonts, @@ -127,7 +148,8 @@ cover: (..args) => { if doctype == "proposal" or doctype == "midterm" { nonfinal-cover( - doctype: doctype, + is-midterm: doctype == "midterm", + degree: degree, anonymous: anonymous, twoside: twoside, ..args, @@ -136,7 +158,7 @@ ) } else if doctype == "final" { final-cover( - doctype: doctype, + degree: degree, anonymous: anonymous, twoside: twoside, ..args, @@ -168,7 +190,7 @@ abstract: (..args) => { if doctype == "final" { abstract( - doctype: doctype, + degree: degree, anonymous: anonymous, twoside: twoside, ..args, @@ -186,7 +208,7 @@ abstract-en: (..args) => { if doctype == "final" { abstract-en( - doctype: doctype, + degree: degree, anonymous: anonymous, twoside: twoside, ..args, @@ -211,20 +233,32 @@ // 插图目录页 list-of-figures: (..args) => { - list-of-figures( - twoside: twoside, - ..args, - fonts: fonts + args.named().at("fonts", default: (:)), - ) + if doctype == "final" { + list-of-figures( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + ) + } else if doctype == "proposal" or doctype == "midterm" { + [] + } else { + panic("not yet been implemented.") + } }, // 表格目录页 list-of-tables: (..args) => { - list-of-tables( - twoside: twoside, - ..args, - fonts: fonts + args.named().at("fonts", default: (:)), - ) + if doctype == "final" { + list-of-tables( + twoside: twoside, + ..args, + fonts: fonts + args.named().at("fonts", default: (:)), + ) + } else if doctype == "proposal" or doctype == "midterm" { + [] + } else { + panic("not yet been implemented.") + } }, // 符号表页 @@ -245,11 +279,17 @@ // 致谢页 acknowledgement: (..args) => { - acknowledgement( - anonymous: anonymous, - twoside: twoside, - ..args, - ) + if doctype == "final" { + acknowledgement( + anonymous: anonymous, + twoside: twoside, + ..args, + ) + } else if doctype == "proposal" or doctype == "midterm" { + [] + } else { + panic("not yet been implemented.") + } }, ) } diff --git a/sustech-graduated-thesis/pages/abstract-en.typ b/sustech-graduated-thesis/pages/abstract-en.typ index 8727151..adc7cc5 100644 --- a/sustech-graduated-thesis/pages/abstract-en.typ +++ b/sustech-graduated-thesis/pages/abstract-en.typ @@ -3,13 +3,13 @@ #import "../utils/indent.typ": fake-par #import "../utils/double-underline.typ": double-underline #import "../utils/custom-tablex.typ": gridx, colspanx +#import "../utils/degree-names.typ": degree-types // #import "../utils/invisible-heading.typ": invisible-heading // 研究生英文摘要页 #let abstract-en( // documentclass 传入的参数 - doctype: "master", - degree: "academic", + degree: "MEng", anonymous: false, twoside: false, fonts: (:), diff --git a/sustech-graduated-thesis/pages/abstract.typ b/sustech-graduated-thesis/pages/abstract.typ index f594dc4..693fc51 100644 --- a/sustech-graduated-thesis/pages/abstract.typ +++ b/sustech-graduated-thesis/pages/abstract.typ @@ -8,8 +8,7 @@ // 研究生中文摘要页 #let abstract( // documentclass 传入的参数 - doctype: "master", - degree: "academic", + doctype: "final", anonymous: false, twoside: false, fonts: (:), diff --git a/sustech-graduated-thesis/pages/final-cover.typ b/sustech-graduated-thesis/pages/final-cover.typ index 2e70930..26980c6 100644 --- a/sustech-graduated-thesis/pages/final-cover.typ +++ b/sustech-graduated-thesis/pages/final-cover.typ @@ -1,12 +1,12 @@ #import "../utils/datetime-display.typ": datetime-display, datetime-en-display #import "../utils/justify-text.typ": justify-text #import "../utils/style.typ": 字号, 字体 +#import "../utils/degree-names.typ": degree-types // 硕士研究生封面 #let final-cover( // documentclass 传入的参数 - doctype: "master", - degree: "academic", + degree: "MEng", nl-cover: false, anonymous: false, twoside: false, @@ -69,13 +69,7 @@ info.bottom-date = datetime-display(info.bottom-date) } // 2.4 处理 degree - if (info.degree == auto) { - if (doctype == "doctor") { - info.degree = "工程博士" - } else { - info.degree = "工程硕士" - } - } + info.degree = degree-types.at(degree) // 3. 内置辅助函数 let info-key(body, info-inset: info-inset, is-meta: false) = { @@ -159,7 +153,7 @@ // 将中文之间的空格间隙从 0.25 em 调整到 0.5 em text(size: 28pt, font: fonts.宋体, spacing: 200%, weight: "bold", - if doctype == "doctor" { "博 士 学 位 论 文" } else { "硕 士 学 位 论 文" }, + if degree == "PhD" { "博 士 学 位 论 文" } else { "硕 士 学 位 论 文" }, ) if (anonymous) { @@ -176,16 +170,8 @@ ..info.title.map((s) => info-value("title", s)).intersperse(info-key(" ")), info-key("作者姓名"), info-value("author", info.author), - ..(if degree == "professional" {( - { - set text(font: fonts.楷体, size: 字号.三号, weight: "bold") - move(dy: 0.3em, scale(x: 55%, box(width: 10em, "专业学位类别(领域)"))) - }, - info-value("major", info.degree + "(" + info.major + ")"), - )} else {( - info-key("专业名称"), - info-value("major", info.major), - )}), + info-key("专业名称"), + info-value("major", info.major), info-key("研究方向"), info-value("field", info.field), info-key("导师姓名"), @@ -270,7 +256,7 @@ v(6pt) - smallcaps(if doctype == "doctor" { "Doctor of phlosophy" } else { "Master" }) + smallcaps(info.degree.at(1)) v(6pt) diff --git a/sustech-graduated-thesis/pages/nonfinal-cover.typ b/sustech-graduated-thesis/pages/nonfinal-cover.typ index 3ee42d2..917c9b9 100644 --- a/sustech-graduated-thesis/pages/nonfinal-cover.typ +++ b/sustech-graduated-thesis/pages/nonfinal-cover.typ @@ -6,7 +6,8 @@ // 硕士研究生封面 #let nonfinal-cover( // documentclass 传入的参数 - doctype: "midterm", + is-midterm: false, + degree: "MEng", anonymous: false, twoside: false, fonts: (:), @@ -95,10 +96,10 @@ ) v(字号.小一) text(size: 字号.小一, font: fonts.宋体, spacing: 100%, weight: "bold", - if info.degree == "PhD" { - if doctype == "midterm" { "博 士 研 究 生 中 期 考 核 报 告" } else { "博 士 研 究 生 开 题 报 告" } + if degree == "PhD" { + if is-midterm { "博 士 研 究 生 中 期 考 核 报 告" } else { "博 士 研 究 生 开 题 报 告" } } else { - if doctype == "midterm" { "硕 士 研 究 生 中 期 考 核 报 告" } else { "硕 士 研 究 生 开 题 报 告" } + if is-midterm { "硕 士 研 究 生 中 期 考 核 报 告" } else { "硕 士 研 究 生 开 题 报 告" } }, ) v(字号.小一) @@ -134,7 +135,7 @@ info-key("学 号"), info-value(info.student-id), - info-key(if doctype == "midterm" {"中 期 考 核 日 期"} else {"开 题 报 告 日 期"}), + info-key(if is-midterm {"中 期 考 核 日 期"} else {"开 题 报 告 日 期"}), info-value(info.submit-date), )) diff --git a/sustech-graduated-thesis/utils/eq-wrap.typ b/sustech-graduated-thesis/utils/eq-wrap.typ new file mode 100644 index 0000000..3244464 --- /dev/null +++ b/sustech-graduated-thesis/utils/eq-wrap.typ @@ -0,0 +1,58 @@ +#let arounds_default = "、,。!?…;:—‘’“”()【】《》~!@#$%^&*()-_=+/*[]{}|\\:;'\"<>,./?  " + + // 3.5 inline 公式两边的空格,必须放在最后 + #let eq-wrap(cont, arounds: arounds_default) = { + if not cont.has("children") { + return cont + } + let cont-fn = cont.func() + let cont-fs = cont.fields() + let _ = cont-fs.remove("children") + + let map-fn = ((n, elem)) => { + if elem.func() in (list.item, enum.item, figure, table, table.cell) { + let fs = elem.fields() + let _ = fs.remove("body") + let lab = if "label" in fs {fs.remove("label")} + let _ = if "caption" in fs {fs.insert("caption", eq-wrap(elem.caption.body))} + let fn = elem.func() + let wrapped = eq-wrap(elem.body) + if lab != none and elem.func() in (list.item, enum.item, figure) { + [#fn(wrapped, ..fs)#lab] + } else { + fn(wrapped, ..fs) + } + } else { + if (elem.func() == math.equation and elem.block == false) or repr(elem) == "context()" { + if n > 0 { + if cont.children.at(n - 1).has("text") { + let prev = cont.children.at(n - 1).text.last() + if not arounds.contains(prev) { + [ ] + } + } + } + elem + if n < cont.children.len() - 1 { + if cont.children.at(n + 1).has("text") { + let next = cont.children.at(n + 1).text.first() + if not arounds.contains(next) { + [ ] + } + } + } + } else { + elem + } + } + } + + if repr(cont-fn) == "sequence" { + for (n, elem) in cont.children.enumerate() { + map-fn((n, elem)) + } + } else { + let new-child = cont.children.enumerate().map(map-fn) + cont-fn(..new-child, ..cont-fs) + } + } \ No newline at end of file