├── LICENSE ├── README.md ├── README.zh.md ├── examples ├── beamer-sms.pdf ├── beamer-sms.typ └── figures │ ├── figure1.png │ ├── figure10.png │ ├── figure11.png │ ├── figure2.png │ ├── figure3.png │ ├── figure4.png │ ├── figure6.png │ ├── figure7.png │ ├── figure8.png │ └── figure9.png └── themes ├── assets ├── seu-background-min.svg ├── seu-background.svg ├── seu-logo-min.svg ├── seu-logo.svg ├── seu-title-bl-white-embed-min.svg ├── seu-title-bl-white-embed.svg ├── seu-title-bl-white-min.svg ├── seu-title-bl-white.svg ├── seu-title-min.svg └── seu-title.svg └── seu-beamer.typ /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] [QuadnucYard] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Touying Slide Theme for Southeast University 2 | 3 | [[中文]](./README.zh.md) 4 | 5 | SEU-styled slide themes and templates based on [Touying](https://touying-typ.github.io/touying/zh/)。 6 | 7 | ## Themes 8 | 9 | So far, only a theme inspired by is provided. The example can be found in `examples/beamer-sms.typ`. 10 | 11 | Welcome to contribute more templates! 12 | 13 | ## License 14 | 15 | Licensed under the [MIT License](LICENSE). 16 | 17 | ## See also 18 | 19 | - seu-thesis-typst by [TideDra](https://github.com/TideDra): 20 | - SEU-Typst-Template by [csimide](https://github.com/csimide): 21 | - seuthesis2024b by [Teddy-van-Jerry](https://github.com/Teddy-van-Jerry): 22 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | # 东南大学幻灯片模板(typst) 2 | 3 | [[English]](./README.md) 4 | 5 | 基于 [Touying](https://touying-typ.github.io/touying/zh/) 的 university 风格实现的东南大学幻灯片模板(typst)。 6 | 7 | ## 主题 8 | 9 | 目前只提供了一个 Beamer 风格的主题 `themes/seu-beamer.typ`,仿自 ,在 `examples/beamer-sms.typ` 中提供迁移示例。 10 | 11 | 欢迎投稿更多模板! 12 | 13 | ## License 14 | 15 | Licensed under the [MIT License](LICENSE). 16 | 17 | ## 友情链接 18 | 19 | - seu-thesis-typst by [TideDra](https://github.com/TideDra): 20 | - SEU-Typst-Template by [csimide](https://github.com/csimide): 21 | - seuthesis2024b by [Teddy-van-Jerry](https://github.com/Teddy-van-Jerry): 22 | -------------------------------------------------------------------------------- /examples/beamer-sms.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/beamer-sms.pdf -------------------------------------------------------------------------------- /examples/beamer-sms.typ: -------------------------------------------------------------------------------- 1 | #import "@preview/touying:0.4.2": * 2 | #import "@preview/unify:0.6.0": num 3 | #import "/themes/seu-beamer.typ" as theme-seu 4 | 5 | #let s = theme-seu.register(aspect-ratio: "4-3") 6 | #let s = (s.methods.info)( 7 | self: s, 8 | title: [Sending out an SMS: Characterizing the Security of the SMS Ecosystem with Public Gateways], 9 | short-title: [Sending out an SMS], 10 | subtitle: [利用公共网关的SMS生态系统的安全性描述], 11 | author: [QuadnucYard], 12 | date: datetime.today(), 13 | institution: [Florida Institute for Cybersecurity Research (FICS)\ 14 | University of Florida], 15 | ) 16 | #let (init, slides, touying-outline, alert, speaker-note, tblock) = utils.methods(s) 17 | #show: init 18 | 19 | // #show strong: alert 20 | 21 | #let (slide, empty-slide, title-slide, outline-slide, new-section-slide, ending-slide) = utils.slides(s) 22 | #show: slides.with(title-slide: false) 23 | 24 | #title-slide(authors: [Bradley Reaves, Nolen Scaife, Dave Tian, Logan Blue, \ 25 | Patrick Traynor and Kevin R.B. Butler \ 26 | 27 | #set text(size: 0.9em) 28 | {reaves, scaife, daveti, bluel}\@ufl.edu \ {traynor, butler}\@cise.ufl.edu 29 | ]) 30 | 31 | #outline-slide() 32 | 33 | = 引言 34 | 35 | == 引言 36 | 37 | #tblock(title: [研究背景])[ 38 | - 短信息(SMS)成为现代通讯的重要组成部分 39 | - 很多组织或网站使用短信息作为身份验证的辅助通道 40 | - 现代短消息的发送,在抵达终端之前不接触蜂窝网络 41 | ] 42 | 43 | #tblock(title: [主要工作])[ 44 | - 对SMS数据进行迄今为止最大的挖掘分析 45 | - 评估良性短消息服务的安全态势 46 | - 刻画通过SMS网关进行的恶意行为 47 | ] 48 | 49 | #new-section-slide(short-title: [系统])[现代SMS生态系统] 50 | 51 | == 短消息服务中心(SMSC) 52 | 53 | #figure( 54 | image("figures/figure1.png", width: 90%), 55 | caption: [短消息服务中心], 56 | ) 57 | 58 | *短消息服务中心*通过运营商网络路由消息,是SMS系统的核心。\ 59 | SMSC接受文本消息,并将消息转发到蜂窝网络中的移动用户。 60 | 61 | == 外部短消息实体(ESME) 62 | 63 | #figure( 64 | image("figures/figure2.png", width: 90%), 65 | caption: [外部短消息实体], 66 | ) 67 | 68 | *外部短消息实体*为外部组织提供针对运营商网络的短消息接入服务。\ 69 | ESME可以用于紧急通报、慈善捐款、或接受一次性验证码等功能。 70 | 71 | == OTT服务 72 | 73 | #figure( 74 | image("figures/figure3.png", width: 90%), 75 | caption: [OTT服务], 76 | ) 77 | 78 | *OTT服务*支持在数据网络上提供短信和语音等第三方服务。\ 79 | OTT可以使用云服务来存储和同步SMS到用户的其他设备。 80 | 81 | #new-section-slide(short-title: [方法])[研究方法与数据集特征] 82 | 83 | == 爬取公共短消息网关 84 | 85 | #slide[ 86 | #set text(0.8em) 87 | 88 | - 使用Scrapy框架爬取公共网关 89 | - 收集8个公共短信网关在14个月的数据 90 | - 共抓取 #num(386327) 条数据 91 | ][ 92 | #show table: set text(0.8em) 93 | 94 | #figure( 95 | table( 96 | columns: 2, 97 | [*Site*], [*Messages*], 98 | [receivesmsonline.net], [81313], 99 | [receive-sms-online.info], [69389], 100 | [receive-sms-now.com], [63797], 101 | [hs3x.com], [55499], 102 | [receivesmsonline.com], [44640], 103 | [receivefreesms.com], [37485], 104 | [receive-sms-online.com], [27094], 105 | [e-receivesms.com], [7107], 106 | ), 107 | caption: [公共网关及抓取的信息数], 108 | ) 109 | ] 110 | 111 | == 消息聚类分析 112 | 113 | #tblock(title: [基本思路])[ 114 | - 使用编辑距离矩阵将类似的消息归于一张连通图中。 115 | - 使用固定值替换感兴趣的消息,如代码、email地址。 116 | - 查找归一化距离小于阈值的消息,并确定聚类边界。 117 | ] 118 | 119 | #tblock(title: [实现步骤])[ 120 | + 加载所有消息。 121 | + 用固定的字符串替换数字、电子邮件和URL以预处理消息。 122 | + 将预处理后的信息按字母排序。 123 | + 通过使用编辑距离阈值(0.9)来确定聚类边界。 124 | + 手动标记各个聚类,以确定服务提供者、消息类别等。 125 | ] 126 | 127 | == 消息分类结果 128 | 129 | - *账户创建确认信息*:向来自服务提供者的用户提供了一个代码,该服务提供者需要在新帐户创建期间进行SMS验证。 130 | - *活动确认信息*:向来自服务提供者的用户提供了请求授权进行活动的代码(例如,付款确认)。 131 | - *一次性密码*:包含用户登录的代码的短信息。 132 | - *用于绑定不同设备的一次性口令*:将消息发送给用户,以绑定一个新的电话号码或启用相应的移动应用程序。 133 | - *重置密码口令*:包含密码重置密码的短信息。 134 | - *其他*:其他未被指定为某种特定功能的消息。 135 | 136 | == 消息分类结果 137 | 138 | #slide[ 139 | #set text(0.8em) 140 | 141 | - 账户创建和移动设备绑定占比最大,占51.6% 142 | - 一次性密码信息占7.6% 143 | - 密码重置消息占1.3% 144 | - 包含“测试”关键词的消息占0.8% 145 | ][ 146 | #show table: set text(0.8em) 147 | 148 | #figure( 149 | image("figures/figure4.png"), 150 | caption: [消息的聚类], 151 | ) 152 | ] 153 | 154 | #new-section-slide(short-title: [分析])[SMS使用情况分析] 155 | 156 | == 使用SMS作为安全信道 157 | 158 | #tblock(title: [PII和其他敏感信息])[ 159 | - 财务信息 160 | - 用户名和密码 161 | - 重置密码口令 162 | - 其他个人识别信息(PII) 163 | - 敏感程序的SMS活动 164 | ] 165 | 166 | == 使用SMS作为安全信道 167 | 168 | #tblock(title: [SMS编码熵])[ 169 | 使用 $chi^2$ 检验测试每组编码的熵。$chi^2$ 检验是一个零假设的显著性检验,用于测试SMS服务的编码是否是从低位到高位均匀分布的。若 $p$ 值小于 $0.01$,则表明观测值和理想均匀分布之间存在统计学上的显著差异。 170 | 检验结果表明,$65%$ 的SMS服务的编码熵较低,容易被预测和攻击。 171 | ] 172 | 173 | #grid( 174 | columns: (1fr, 1fr, 1fr), 175 | figure( 176 | image("figures/figure6.png"), 177 | caption: [WeChat], 178 | ), 179 | figure( 180 | image("figures/figure7.png"), 181 | caption: [Talk2], 182 | ), 183 | figure( 184 | image("figures/figure8.png"), 185 | caption: [Google], 186 | ), 187 | ) 188 | 189 | == SMS的恶意应用 190 | 191 | #tblock(title: [公共网关检测到的恶意信息])[ 192 | - *泄露用户位置信息*:短URL可以用于确定消息的源和目的地,即会泄漏用户的位置信息。 193 | - *垃圾邮件宣传广告*:在公共网关服务中比例较低,约为1.0%。 194 | - *网络钓鱼活动*:试图欺骗用户,使其相信自己正与合法网站通信。 195 | ] 196 | 197 | #grid( 198 | columns: (1fr, 1fr, 1fr), 199 | figure( 200 | image("figures/figure9.png"), 201 | caption: [SMS地址分布], 202 | ), 203 | figure( 204 | image("figures/figure10.png"), 205 | caption: [钓鱼短信实例], 206 | ), 207 | figure( 208 | image("figures/figure11.png"), 209 | caption: [钓鱼网站], 210 | ), 211 | ) 212 | 213 | = 结论 214 | 215 | == 结论 216 | 217 | - SMS生态系统在智能手机时代出现了新的发展,加入了更多新的设备和参与者。 218 | - 公共网关为用户提供了基于SMS的各种安全解决方案。 219 | - 根据该研究,将SMS作为安全信道传递敏感信息存在一定的危险性。一些一次性的消息传递机制亟待改进。 220 | - 至于短信滥用,公共网关可以用于规避一些安全性较差的认证机制,或进行PVA欺诈行为。 221 | 222 | #ending-slide(title: [Thanks for Listening.])[ 223 | Touying theme for Southeast University 224 | 225 | https://github.com/QuadnucYard/touying-theme-seu 226 | 227 | Welcome Star and Fork! 228 | ] 229 | -------------------------------------------------------------------------------- /examples/figures/figure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure1.png -------------------------------------------------------------------------------- /examples/figures/figure10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure10.png -------------------------------------------------------------------------------- /examples/figures/figure11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure11.png -------------------------------------------------------------------------------- /examples/figures/figure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure2.png -------------------------------------------------------------------------------- /examples/figures/figure3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure3.png -------------------------------------------------------------------------------- /examples/figures/figure4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure4.png -------------------------------------------------------------------------------- /examples/figures/figure6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure6.png -------------------------------------------------------------------------------- /examples/figures/figure7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure7.png -------------------------------------------------------------------------------- /examples/figures/figure8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure8.png -------------------------------------------------------------------------------- /examples/figures/figure9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuadnucYard/touying-theme-seu/f61050b8c50155538f2f2d86520ea66987875ccb/examples/figures/figure9.png -------------------------------------------------------------------------------- /themes/assets/seu-background-min.svg: -------------------------------------------------------------------------------- 1 | 南京 -------------------------------------------------------------------------------- /themes/assets/seu-logo-min.svg: -------------------------------------------------------------------------------- 1 | 南京 -------------------------------------------------------------------------------- /themes/assets/seu-title-bl-white-embed-min.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/assets/seu-title-bl-white-embed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 43 | -------------------------------------------------------------------------------- /themes/assets/seu-title-bl-white-min.svg: -------------------------------------------------------------------------------- 1 | SOUTHEAST UNIVERSITY -------------------------------------------------------------------------------- /themes/assets/seu-title-bl-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SOUTHEAST UNIVERSITY 45 | -------------------------------------------------------------------------------- /themes/assets/seu-title-min.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/assets/seu-title.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 51 | -------------------------------------------------------------------------------- /themes/seu-beamer.typ: -------------------------------------------------------------------------------- 1 | // University theme - modified to fit SEU 2 | // Originally contributed by Pol Dellaiera - https://github.com/drupol 3 | 4 | #import "@preview/touying:0.4.2": * 5 | #import "@preview/cuti:0.2.1": show-cn-fakebold 6 | 7 | #let seu-nav-bar(self: none) = states.touying-progress-with-sections(dict => { 8 | let (current-sections, final-sections) = dict 9 | current-sections = current-sections.filter(section => section.loc != none).map(section => ( 10 | section, 11 | section.children, 12 | )).flatten().filter(item => item.kind == "section") 13 | final-sections = final-sections.filter(section => section.loc != none).map(section => ( 14 | section, 15 | section.children, 16 | )).flatten().filter(item => item.kind == "section") 17 | let current-index = current-sections.len() - 1 18 | 19 | set text(size: 0.5em) 20 | for (i, section) in final-sections.enumerate() { 21 | set text(fill: if i != current-index { 22 | gray 23 | } else { 24 | white 25 | }) 26 | box(inset: 0.5em)[#link(section.loc, utils.section-short-title(section))] 27 | } 28 | }) 29 | 30 | #let seu-outline(self: none) = states.touying-progress-with-sections(dict => { 31 | let (current-sections, final-sections) = dict 32 | current-sections = current-sections.filter(section => section.loc != none).map(section => ( 33 | section, 34 | section.children, 35 | )).flatten().filter(item => item.kind == "section") 36 | final-sections = final-sections.filter(section => section.loc != none).map(section => ( 37 | section, 38 | section.children, 39 | )).flatten().filter(item => item.kind == "section") 40 | let current-index = current-sections.len() - 1 41 | 42 | // set text(size: 0.5em) 43 | for (i, section) in final-sections.enumerate() { 44 | if i == 0 { 45 | continue 46 | } 47 | set text(fill: if current-index == 0 or i == current-index { 48 | self.colors.primary 49 | } else { 50 | self.colors.primary.lighten(80%) 51 | }) 52 | block( 53 | spacing: 1.5em, 54 | [#link(section.loc, utils.section-short-title(section))], 55 | ) 56 | } 57 | }) 58 | 59 | #let slide( 60 | self: none, 61 | title: auto, 62 | subtitle: auto, 63 | header: auto, 64 | footer: auto, 65 | display-current-section: auto, 66 | ..args, 67 | ) = { 68 | if title != auto { 69 | self.seu-title = title 70 | } 71 | if subtitle != auto { 72 | self.seu-subtitle = subtitle 73 | } 74 | if header != auto { 75 | self.seu-header = header 76 | } 77 | if footer != auto { 78 | self.seu-footer = footer 79 | } 80 | if display-current-section != auto { 81 | self.seu-display-current-section = display-current-section 82 | } 83 | (self.methods.touying-slide)( 84 | ..args.named(), 85 | self: self, 86 | title: title, 87 | setting: body => { 88 | show: args.named().at("setting", default: body => body) 89 | // align(center + horizon, block(body)) 90 | align(horizon, body) 91 | }, 92 | ..args.pos(), 93 | ) 94 | } 95 | 96 | #let title-slide(self: none, ..args) = { 97 | let info = self.info + args.named() 98 | info.authors = { 99 | let authors = if "authors" in info { 100 | info.authors 101 | } else { 102 | info.author 103 | } 104 | if type(authors) == array { 105 | authors 106 | } else { 107 | (authors,) 108 | } 109 | } 110 | let content = { 111 | if info.logo != none { 112 | align(right, info.logo) 113 | } 114 | show: align.with(center + horizon) 115 | block( 116 | fill: self.colors.primary, 117 | inset: 1.5em, 118 | radius: 0.5em, 119 | breakable: false, 120 | { 121 | text(size: 1.2em, fill: self.colors.neutral-lightest, weight: "bold", info.title) 122 | if info.subtitle != none { 123 | parbreak() 124 | text(size: 1.0em, fill: self.colors.neutral-lightest, weight: "bold", info.subtitle) 125 | } 126 | }, 127 | ) 128 | // authors 129 | grid( 130 | columns: (1fr,) * calc.min(info.authors.len(), 3), 131 | column-gutter: 1em, 132 | row-gutter: 1em, 133 | ..info.authors.map(author => text(fill: black, author)), 134 | ) 135 | v(0.5em) 136 | // institution 137 | if info.institution != none { 138 | parbreak() 139 | text(size: 0.7em, info.institution) 140 | } 141 | // date 142 | if info.date != none { 143 | parbreak() 144 | text(size: 1.0em, utils.info-date(self)) 145 | } 146 | } 147 | (self.methods.touying-slide)(self: self, repeat: none, content) 148 | } 149 | 150 | #let outline-slide(self: none) = { 151 | self.seu-title = [目录] 152 | let content = { 153 | set align(horizon) 154 | set text(weight: "bold") 155 | hide([-]) 156 | seu-outline(self: self) 157 | } 158 | (self.methods.touying-slide)(self: self, repeat: none, section: (title: [目录]), content) 159 | } 160 | 161 | #let new-section-slide(self: none, short-title: auto, title) = { 162 | self.seu-title = [目录] 163 | let content = { 164 | set align(horizon) 165 | set text(weight: "bold") 166 | hide([-]) // 如果没这个会导致显示出问题 167 | seu-outline(self: self) 168 | } 169 | (self.methods.touying-slide)(self: self, repeat: none, section: (title: title, short-title: short-title), content) 170 | } 171 | 172 | #let ending-slide(self: none, title: none, body) = { 173 | let content = { 174 | set align(center + horizon) 175 | if title != none { 176 | block( 177 | fill: self.colors.tertiary, 178 | inset: (x: 3em, y: 0.7em), 179 | radius: 0.5em, 180 | text(size: 1.5em, fill: self.colors.neutral-lightest, title), 181 | ) 182 | } 183 | body 184 | } 185 | (self.methods.touying-slide)(self: self, repeat: none, content) 186 | } 187 | 188 | #let slides(self: none, title-slide: true, slide-level: 1, ..args) = { 189 | if title-slide { 190 | (self.methods.title-slide)(self: self) 191 | } 192 | (self.methods.touying-slides)(self: self, slide-level: slide-level, ..args) 193 | } 194 | 195 | #let register( 196 | self: themes.default.register(), 197 | aspect-ratio: "16-9", 198 | progress-bar: true, 199 | display-current-section: true, 200 | footer-columns: (25%, 25%, 1fr, 5em), 201 | footer-a: self => self.info.author, 202 | footer-b: self => utils.info-date(self), 203 | footer-c: self => if self.info.short-title == auto { 204 | self.info.title 205 | } else { 206 | self.info.short-title 207 | }, 208 | footer-d: self => { 209 | states.slide-counter.display() + " / " + states.last-slide-number 210 | }, 211 | ..args, 212 | ) = { 213 | // color theme 214 | self = (self.methods.colors)( 215 | self: self, 216 | primary: rgb("#4d7c2b"), 217 | primary-dark: rgb("#3d5c27"), 218 | secondary: rgb("#fdd100"), 219 | tertiary: rgb("#006600"), 220 | neutral-lightest: rgb("#ffffff"), 221 | neutral-darkest: rgb("#000000"), 222 | ) 223 | // marker 224 | self.seu-knob-marker = box( 225 | width: 0.5em, 226 | place( 227 | dy: 0.1em, 228 | circle( 229 | fill: gradient.radial( 230 | self.colors.primary.lighten(100%), 231 | self.colors.primary.darken(40%), 232 | focal-center: (30%, 30%), 233 | ), 234 | radius: 0.25em, 235 | ), 236 | ), 237 | ) 238 | 239 | // save the variables for later use 240 | self.seu-enable-progress-bar = progress-bar 241 | self.seu-progress-bar = self => states.touying-progress(ratio => { 242 | grid( 243 | columns: (ratio * 100%, 1fr), 244 | rows: 2pt, 245 | components.cell(fill: self.colors.primary), 246 | components.cell(fill: self.colors.tertiary), 247 | ) 248 | }) 249 | self.seu-navigation = self => { 250 | grid( 251 | align: center + horizon, 252 | columns: (1fr, auto, auto), 253 | rows: 1.8em, 254 | components.cell(fill: self.colors.neutral-darkest, seu-nav-bar(self: self)), 255 | block(fill: self.colors.primary, inset: 0.2em, image("assets/seu-title-bl-white-embed-min.svg", height: 100%)), 256 | block(fill: self.colors.primary, inset: 0.1em, image("assets/seu-logo-min.svg", height: 100%)), 257 | ) 258 | } 259 | self.seu-display-current-section = display-current-section 260 | self.seu-title = none 261 | self.seu-subtitle = none 262 | self.seu-footer = self => { 263 | let cell(fill: none, it) = rect( 264 | width: 100%, 265 | height: 100%, 266 | inset: 1pt, 267 | outset: 0pt, 268 | fill: fill, 269 | stroke: none, 270 | align(horizon, text(fill: self.colors.neutral-lightest, it)), 271 | ) 272 | grid( 273 | columns: footer-columns, 274 | rows: 1.5em, 275 | cell(fill: self.colors.neutral-darkest, utils.call-or-display(self, footer-a)), 276 | cell(fill: self.colors.neutral-darkest, utils.call-or-display(self, footer-b)), 277 | cell(fill: self.colors.primary, utils.call-or-display(self, footer-c)), 278 | cell(fill: self.colors.primary, utils.call-or-display(self, footer-d)), 279 | ) 280 | } 281 | self.seu-header = self => { 282 | if self.seu-title != none { 283 | block( 284 | width: 100%, 285 | height: 1.8em, 286 | fill: gradient.linear(self.colors.primary, self.colors.neutral-darkest), 287 | place( 288 | left + horizon, 289 | text(fill: self.colors.neutral-lightest, weight: "bold", size: 1.3em, self.seu-title), 290 | dx: 1.5em, 291 | ), 292 | ) 293 | } 294 | } 295 | // set page 296 | let header(self) = { 297 | set align(top) 298 | grid( 299 | rows: (auto, auto), 300 | utils.call-or-display(self, self.seu-navigation), 301 | utils.call-or-display(self, self.seu-header), 302 | ) 303 | } 304 | let footer(self) = { 305 | set text(size: .5em) 306 | set align(center + horizon) 307 | utils.call-or-display(self, self.seu-footer) 308 | } 309 | 310 | self.page-args += ( 311 | paper: "presentation-" + aspect-ratio, 312 | header: header, 313 | footer: footer, 314 | header-ascent: 0em, 315 | footer-descent: 0em, 316 | margin: (top: 4em, bottom: 0.7em, x: 2.5em), 317 | background: place(center + horizon, dx: 50%, dy: 5%, image("assets/seu-background-min.svg", width: 75%)), 318 | ) 319 | // register methods 320 | self.methods.slide = slide 321 | self.methods.title-slide = title-slide 322 | self.methods.outline-slide = outline-slide 323 | self.methods.new-section-slide = new-section-slide 324 | self.methods.touying-new-section-slide = new-section-slide 325 | self.methods.ending-slide = ending-slide 326 | self.methods.slides = slides 327 | self.methods.touying-outline = (self: none, enum-args: (:), ..args) => { 328 | states.touying-outline(self: self, enum-args: (tight: false) + enum-args, ..args) 329 | } 330 | self.methods.alert = (self: none, it) => text(fill: self.colors.primary, it) 331 | 332 | self.methods.tblock = (self: none, title: none, it) => { 333 | grid( 334 | columns: 1, 335 | row-gutter: 0pt, 336 | block( 337 | fill: self.colors.primary-dark, 338 | width: 100%, 339 | radius: (top: 0.4em), 340 | inset: (top: 0.4em, bottom: 0.3em, x: 0.5em), 341 | text(fill: self.colors.neutral-lightest, weight: "bold", title), 342 | ), 343 | rect( 344 | fill: gradient.linear(self.colors.primary-dark, self.colors.primary.lighten(90%), angle: 90deg), 345 | width: 100%, 346 | height: 4pt, 347 | ), 348 | block( 349 | fill: self.colors.primary.lighten(90%), 350 | width: 100%, 351 | radius: (bottom: 0.4em), 352 | inset: (top: 0.4em, rest: 0.5em), 353 | it, 354 | ), 355 | ) 356 | } 357 | 358 | self.methods.init = (self: none, text-size: 22pt, body) => { 359 | set text(size: text-size, font: ("Helvetica", "SimHei")) 360 | set block(spacing: 0.8em) 361 | set heading(outlined: false) 362 | set list(marker: self.seu-knob-marker) 363 | 364 | show strong: it => text(weight: "bold", it) 365 | set par(justify: true) 366 | 367 | show figure.caption: set text(size: 0.6em) 368 | show footnote.entry: set text(size: 0.6em) 369 | 370 | show: show-cn-fakebold 371 | 372 | set text(lang: "zh") 373 | show figure.where(kind: table): set figure.caption(position: top) 374 | 375 | body 376 | } 377 | 378 | self 379 | } 380 | --------------------------------------------------------------------------------