feat: added default configuration and configuration validation

This commit is contained in:
Sven Vogel 2024-07-02 10:57:38 +02:00
parent ed9a5beac5
commit 40bf0af544
9 changed files with 146 additions and 63 deletions

View File

@ -6,3 +6,83 @@
// Author: Sven Vogel // Author: Sven Vogel
// Edited: 27.06.2024 // Edited: 27.06.2024
// License: MIT // License: MIT
// default configuration
#let default-config = (
lang: "en",
region: "en",
author: (
name: "Sven Vogel",
semester: 4,
program: "Informationtechnology",
course: "TINF19IT1",
faculty: "Technik",
university: "DHBW Mannheim",
company: "ABB AG",
supervisor: "Benny Goodman",
matriculation-number: 123456789),
thesis: (
title: "Unofficial ABB/DHBW Typst template",
subtitle: "for reports and thesises",
submission-date: "23rd march 2020",
timeframe: "1st january 2020 - 20th march 2020",
kind: "T2000",
summary: none,
abstract: none,
keywords: ( "IT", "other stuff" ),
bibliography: none /* bibliography("refs.bib") */,
glossary: none,
appendices: none),
style: (
header: (
bottom-padding: 1.5em,
underline-top-padding: 0pt,
logo-height: 3em),
page: (
format: "a4",
margin: (
left: 3cm,
right: 2.5cm,
top: 2.5cm,
bottom: 2.5cm)),
text: (
size: 12pt,
font: "Open Sans"),
heading: (
font: "Montserrat"),
link: (
color: red.darken(15%))))
// Insert a dictionary `update` into `base` but only the entries of update that also exist in base
// Runs recursively on all sub dictionaries
#let deep-insert-checked(base, update) = {
if base == none {
panic("target dictionary is none")
}
if update == none {
return base
}
for (key, val) in base {
if key in update {
let update_val = update.at(key)
if type(val) == dictionary and type(update_val) == dictionary {
base.insert(key, deep-insert-checked(val, update_val))
} else if val == none or type(val) == type(update_val) {
base.insert(key, update_val)
} else {
panic("missmatched dictionary entry `" + key + "` type: expected `" + type(val) + "` got `" + type(update_val) + "`")
}
} else {
base.insert(key, val)
}
}
return base
}
#let validate-config(config) = {
return deep-insert-checked(default-config, config)
}

View File

@ -10,7 +10,17 @@
// start of template pages and styles // start of template pages and styles
#let dhbw-template(config: dictionary, body) = [ #let dhbw-template(config: dictionary, body) = [
#import "conf.typ": validate-config
#import "style.typ": global_styled_doc, content_styled, end_styled #import "style.typ": global_styled_doc, content_styled, end_styled
#import "pages/titlepage.typ": new_title_page
#import "pages/declaration-of-authorship.typ": new_declaration_of_authorship
#import "pages/confidentiality-statement.typ": new_confidentiality_statement_page
#import "pages/prerelease-note.typ": new_prerelease_note
#import "pages/outline.typ": new_outline
#import "pages/abstract.typ": new_abstract
#import "pages/appendix.typ": show-appendix
#let config = validate-config(config)
#let doc = body #let doc = body
@ -23,41 +33,25 @@
// apply global style to every element in the argument content // apply global style to every element in the argument content
#global_styled_doc(config: config, body: [ #global_styled_doc(config: config, body: [
#import "pages/titlepage.typ": new_title_page
#import "pages/declaration-of-authorship.typ": new_declaration_of_authorship
#import "pages/confidentiality-statement.typ": new_confidentiality_statement_page
#import "pages/prerelease-note.typ": new_prerelease_note
#import "pages/outline.typ": new_outline
#import "pages/abstract.typ": new_abstract
#import "pages/appendix.typ": show-appendix
// configure text locale // configure text locale
#set text(lang: config.lang, region: config.region) #set text(
lang: config.lang,
region: config.region)
// preppend title page // preppend title page
#new_title_page(config) #new_title_page(config)
// prelude includes: title, declaration of authorship, confidentiality statement, outline and abstract // prelude includes: title, declaration of authorship, confidentiality statement, outline and abstract
// these will have roman page numbers // these will have roman page numbers
#pagebreak(weak: true)
#new_declaration_of_authorship(config) #new_declaration_of_authorship(config)
#pagebreak(weak: true)
#new_confidentiality_statement_page(config) #new_confidentiality_statement_page(config)
#pagebreak(weak: true)
#new_prerelease_note(config) #new_prerelease_note(config)
#pagebreak(weak: true)
#new_abstract(config) #new_abstract(config)
#pagebreak(weak: true)
#new_outline() #new_outline()
// glossary is built inline here because the links must be // glossary is built inline here because the links must be
// exposed to the entire document // exposed to the entire document
#import "@preview/glossarium:0.4.1": make-glossary, print-glossary, gls, glspl #import "@preview/glossarium:0.4.1": make-glossary, print-glossary
#show: make-glossary #show: make-glossary
#pagebreak(weak: true) #pagebreak(weak: true)
@ -74,16 +68,13 @@
// mark end of prelude // mark end of prelude
#metadata("prelude terminate") <end-of-prelude> #metadata("prelude terminate") <end-of-prelude>
#content_styled(config: config, body: [ #content_styled(config: config, body: doc)
// code of document follows here
#doc
])
#metadata("content terminate") <end-of-content> #metadata("content terminate") <end-of-content>
#end_styled(config: config, body: [ #end_styled(config: config, body: [
// add bibliography if set // add bibliography if set
#if config.thesis.bibliography != none { #if "bibliography" in config.thesis and config.thesis.bibliography != none {
pagebreak(weak: true) pagebreak(weak: true)
set bibliography(style: "ieee") set bibliography(style: "ieee")
config.thesis.bibliography config.thesis.bibliography

View File

@ -8,6 +8,8 @@
#let new_abstract(config) = context [ #let new_abstract(config) = context [
#pagebreak(weak: true)
#let thesis = config.thesis #let thesis = config.thesis
#pagebreak(weak: true) #pagebreak(weak: true)

View File

@ -1,6 +1,8 @@
#let new_confidentiality_statement_page(config) = context [ #let new_confidentiality_statement_page(config) = context [
#pagebreak(weak: true)
#let thesis = config.thesis #let thesis = config.thesis
#let author = config.author #let author = config.author

View File

@ -1,6 +1,8 @@
#let new_declaration_of_authorship(config) = context [ #let new_declaration_of_authorship(config) = context [
#pagebreak(weak: true)
#let thesis = config.thesis #let thesis = config.thesis
#let author = config.author #let author = config.author

View File

@ -4,6 +4,7 @@
// can optionally insert a pagebreak after the outline // can optionally insert a pagebreak after the outline
// NOTE: will not render in case the listing is empty // NOTE: will not render in case the listing is empty
#let render_filtered_outline(title: str, kind: selector) = context { #let render_filtered_outline(title: str, kind: selector) = context {
let elems = query(figure.where(kind: kind), here()) let elems = query(figure.where(kind: kind), here())
let count = elems.len() let count = elems.len()
@ -82,8 +83,9 @@
} }
} }
#let new_outline() = { #let new_outline() = {
pagebreak(weak: true)
show outline.entry.where( show outline.entry.where(
level: 1, level: 1,
): it => { ): it => {

View File

@ -1,6 +1,8 @@
#let new_prerelease_note(config) = context [ #let new_prerelease_note(config) = context [
#pagebreak(weak: true)
#let thesis = config.thesis #let thesis = config.thesis
#let author = config.author #let author = config.author

1
src/requirements.typ Normal file
View File

@ -0,0 +1 @@
#import "@preview/glossarium:0.4.1": *

View File

@ -7,40 +7,38 @@
// Edited: 27.06.2024 // Edited: 27.06.2024
// License: MIT // License: MIT
#let HeaderPaddingBottom = 1.5em
#let LogoHeight = 3em
#let HeaderUnderlinePaddingTop = 0pt
// global style of document // global style of document
#let global_styled_doc(config: dictionary, body: content) = context [ #let global_styled_doc(config: dictionary, body: content) = context {
#let thesis = config.thesis let thesis = config.thesis
let style = config.style
// set page geometry // set page geometry
// paper format of A4 // and paper format
#set page( set page(
paper: "a4", paper: style.page.format,
margin: (left: 3cm, right: 2.5cm, top: 2.5cm, bottom: 2.5cm)) margin: style.page.margin)
#set text( set text(
size: 12pt, size: style.text.size,
ligatures: true, ligatures: true,
hyphenate: true, hyphenate: true,
dir: ltr, dir: ltr,
font: "Open Sans") font: style.text.font)
#show heading: set text( show heading: set text(
font: "Montserrat", font: style.heading.font,
weight: "semibold") weight: "semibold")
#set heading(supplement: [chapter]) set heading(supplement: [chapter])
// Set header spacing // Set header spacing
#show heading.where(level: 1): it => v(2em) + it + v(1em) show heading.where(level: 1): it => v(2em) + it + v(1em)
#show heading.where(level: 2): it => v(1em) + it + v(0.5em) show heading.where(level: 2): it => v(1em) + it + v(0.5em)
#show heading.where(level: 3): it => v(0.5em) + it + v(0.25em) show heading.where(level: 3): it => v(0.5em) + it + v(0.25em)
#set raw(tab-size: 4, theme: "res/github.tmTheme") // set theme for code blocks
#show raw.where(block: true): code => { set raw(tab-size: 4, theme: "res/github.tmTheme")
show raw.where(block: true): code => {
show raw.line: line => { show raw.line: line => {
text(fill: gray)[#line.number] text(fill: gray)[#line.number]
h(1em) h(1em)
@ -49,20 +47,23 @@
code code
} }
#set block(spacing: 2em) set block(spacing: 2em)
#set par( set par(
justify: true, justify: true,
first-line-indent: 1em, first-line-indent: 1em,
leading: 1em) leading: 1em)
#show link: set text(fill: red.darken(15%)) // give links a color
#show ref: set text(fill: red.darken(15%)) show link: set text(fill: style.link.color)
show ref: set text(fill: style.link.color)
#set heading(numbering: none) set heading(numbering: none)
#set page( set page(
header-ascent: HeaderUnderlinePaddingTop + HeaderPaddingBottom, header-ascent: style.header.underline-top-padding + style.header.bottom-padding,
footer-descent: 1em, footer-descent: 1em,
margin: (top: 2.5cm + LogoHeight + HeaderUnderlinePaddingTop + HeaderPaddingBottom, bottom: 2.5cm + 1em), margin: (
top: style.page.margin.top + style.header.logo-height + style.header.underline-top-padding + style.header.bottom-padding,
bottom: style.page.margin.bottom + 1em),
numbering: (..nums) => { numbering: (..nums) => {
let current-page = here().page() let current-page = here().page()
if current-page == 1{ if current-page == 1{
@ -84,9 +85,9 @@
// we need two, so make both half the page width // we need two, so make both half the page width
columns: (50%, 50%), columns: (50%, 50%),
// left align logo of ABB // left align logo of ABB
align(left, image("res/ABB.svg", height: LogoHeight)), align(left, image("res/ABB.svg", height: style.header.logo-height)),
// right align logo of DHBW // right align logo of DHBW
align(right, image("res/DHBW.svg", height: LogoHeight))) align(right, image("res/DHBW.svg", height: style.header.logo-height)))
} else if query(<end-of-prelude>).first().location().page() <= here().page() { } else if query(<end-of-prelude>).first().location().page() <= here().page() {
let headers-before = query(selector(heading.where(numbering: "1.", level: 1)).before(here())) let headers-before = query(selector(heading.where(numbering: "1.", level: 1)).before(here()))
@ -107,24 +108,24 @@
columns: (1fr, auto), columns: (1fr, auto),
align: (horizon, bottom), align: (horizon, bottom),
context [ _ #header-title _ ], context [ _ #header-title _ ],
image("res/DHBW.svg", height: LogoHeight)) image("res/DHBW.svg", height: style.header.logo-height))
v(HeaderUnderlinePaddingTop - 1em) v(style.header.underline-top-padding - 1em)
line(length: 100%) line(length: 100%)
} else { } else {
grid( grid(
columns: (1fr, auto), columns: (1fr, auto),
align: (horizon, bottom), align: (horizon, bottom),
context [ _ #config.thesis.title _ ], context [ _ #config.thesis.title _ ],
image("res/DHBW.svg", height: LogoHeight) image("res/DHBW.svg", height: style.header.logo-height)
) )
v(HeaderUnderlinePaddingTop - 1em) v(style.header.underline-top-padding - 1em)
line(length: 100%) line(length: 100%)
} }
}) })
#body body
] }
#let content_styled(config: dictionary, body: content) = [ #let content_styled(config: dictionary, body: content) = [
#set heading(numbering: "1.") #set heading(numbering: "1.")