diff --git a/src/branding.typ b/src/branding.typ index c80ca6c..079a875 100644 --- a/src/branding.typ +++ b/src/branding.typ @@ -3,16 +3,16 @@ // https://brand.abb/portal/en/branding-principles/basic-brand-elements/color // ABB branding colors -#let ABB-RED = cmyk(0%, 100%, 95%, 0%) -#let ABB-BLACK = cmyk(0%, 0%, 0%, 100%) +#let ABB-RED = cmyk(0%, 100%, 95%, 0%) +#let ABB-BLACK = cmyk(0%, 0%, 0%, 100%) #let ABB-GRAY-01 = cmyk(0%, 0%, 0%, 90%) #let ABB-GRAY-02 = cmyk(0%, 0%, 0%, 75%) #let ABB-GRAY-03 = cmyk(0%, 0%, 0%, 55%) #let ABB-GRAY-04 = cmyk(0%, 0%, 0%, 35%) #let ABB-GRAY-05 = cmyk(0%, 0%, 0%, 15%) #let ABB-GRAY-06 = cmyk(0%, 0%, 0%, 5%) -#let ABB-WHITE = cmyk(0%, 0%, 0%, 0%) +#let ABB-WHITE = cmyk(0%, 0%, 0%, 0%) // ABB branding functinal colors -#let ABB-BLUE = cmyk(100%, 53%, 2%, 16%) -#let ABB-GREEN = cmyk(91%, 4%, 100%, 25%) +#let ABB-BLUE = cmyk(100%, 53%, 2%, 16%) +#let ABB-GREEN = cmyk(91%, 4%, 100%, 25%) #let ABB-YELLOW = cmyk(0%, 9%, 100%, 0%) diff --git a/src/conf.typ b/src/conf.typ index cfc76ed..ae2ba05 100644 --- a/src/conf.typ +++ b/src/conf.typ @@ -53,9 +53,9 @@ page: ( format: "a4", margin: ( - left: 3cm, - right: 2.5cm, - top: 2.5cm, + left: 3cm, + right: 2.5cm, + top: 2.5cm, bottom: 2.5cm)), text: ( size: 12pt, @@ -81,20 +81,20 @@ } for (key, val) in base { - if key in update { - let update_val = update.at(key) + 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) + "`") - } + 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 { - base.insert(key, val) + panic("missmatched dictionary entry `" + key + "` type: expected `" + type(val) + "` got `" + type(update_val) + "`") } + } else { + base.insert(key, val) } + } return base } diff --git a/src/glossarium.typ b/src/glossarium.typ index 5fb8bae..143e12d 100644 --- a/src/glossarium.typ +++ b/src/glossarium.typ @@ -30,7 +30,10 @@ SOFTWARE.*/ #let __query_labels_with_key(loc, key, before: false) = { if before { query( - selector(label(__glossary_label_prefix + key)).before(loc, inclusive: false), + selector(label(__glossary_label_prefix + key)).before( + loc, + inclusive: false, + ), loc, ) } else { @@ -40,7 +43,7 @@ SOFTWARE.*/ // key not found error #let __not-found-panic-error-msg(key) = { - __glossarium_error_prefix+"key '"+key+"' not found" + __glossarium_error_prefix + "key '" + key + "' not found" } // Reference a term @@ -49,20 +52,25 @@ SOFTWARE.*/ let __glossary_entries = __glossary_entries.final(here()) if key in __glossary_entries { let entry = __glossary_entries.at(key) - + let gloss = __query_labels_with_key(here(), key, before: true) - + let is_first = gloss == () let entlong = entry.at("long", default: "") let textLink = if display != none { [#display] - } else if (is_first or long == true) and entlong != [] and entlong != "" and long != false { + } else if ( + is_first or long == true + ) and entlong != [] and entlong != "" and long != false { [#entlong (#entry.short#suffix)] } else { [#entry.short#suffix] } - - [#link(label(entry.key), textLink)#label(__glossary_label_prefix + entry.key)] + + [#link( + label(entry.key), + textLink, + )#label(__glossary_label_prefix + entry.key)] } else { panic(__not-found-panic-error-msg(key)) } @@ -76,14 +84,14 @@ SOFTWARE.*/ let __glossary_entries = __glossary_entries.final(here()) if key in __glossary_entries { let entry = __glossary_entries.at(key) - + let gloss = __query_labels_with_key(here(), key, before: true) - + let is_first = gloss == () - let entlongplural = entry.at("longplural", default: ""); + let entlongplural = entry.at("longplural", default: "") let entlong = if entlongplural == [] or entlongplural == "" { // if the entry long plural is not provided, then fallback to adding 's' suffix - let entlong = entry.at("long", default: ""); + let entlong = entry.at("long", default: "") if entlong != [] and entlong != "" { [#entlong#suffix] } else { @@ -92,21 +100,26 @@ SOFTWARE.*/ } else { [#entlongplural] } - - let entplural = entry.at("plural", default: ""); + + let entplural = entry.at("plural", default: "") let short = if entplural == [] or entplural == "" { [#entry.short#suffix] } else { [#entplural] } - - let textLink = if (is_first or long == true) and entlong != [] and entlong != "" and long != false { + + let textLink = if ( + is_first or long == true + ) and entlong != [] and entlong != "" and long != false { [#entlong (#short)] } else { [#short] } - - [#link(label(entry.key), textLink)#label(__glossary_label_prefix + entry.key)] + + [#link( + label(entry.key), + textLink, + )#label(__glossary_label_prefix + entry.key)] } else { panic(__not-found-panic-error-msg(key)) } @@ -152,69 +165,73 @@ SOFTWARE.*/ for entry in entry_list { x.insert(entry.key, entry) } - + x }) let groups = entries.map(x => x.at("group")).dedup() - + for group in groups.sorted() { if group != "" [#heading(group, level: 1, outlined: false) ] for entry in entries.sorted(key: x => x.key) { if entry.group == group { [ #show figure.where(kind: __glossarium_figure): it => it.caption - #figure( - supplement: "", - kind: __glossarium_figure, - numbering: none, - caption: { - context { - let term_references = __query_labels_with_key(here(), entry.key) - if term_references.len() != 0 or show-all { - let desc = entry.at("desc", default: "") - let long = entry.at("long", default: "") - let hasLong = long != "" and long != [] - let hasDesc = desc != "" and desc != [] - grid( - columns: 2, - column-gutter: 1em, - text(weight: "bold", entry.short), - { - if hasLong { - text(weight: "bold", entry.long) - } - if hasLong and hasDesc [:] - if hasDesc [ #desc ] else [. ] - if disable-back-references != true { - term_references.map(x => x.location()).sorted(key: x => x.page()).fold( - (values: (), pages: ()), - ((values, pages), x) => if pages.contains(x.page()) { - (values: values, pages: pages) - } else { - values.push(x) - pages.push(x.page()) - (values: values, pages: pages) - }, - ).values.map(x => { - let page-numbering = x.page-numbering(); - if page-numbering == none { - page-numbering = "1" - } - link(x)[#numbering(page-numbering, ..counter(page).at(x))] - } - ).join(", ") - } + #figure( + supplement: "", + kind: __glossarium_figure, + numbering: none, + caption: { + context { + let term_references = __query_labels_with_key(here(), entry.key) + if term_references.len() != 0 or show-all { + let desc = entry.at("desc", default: "") + let long = entry.at("long", default: "") + let hasLong = long != "" and long != [] + let hasDesc = desc != "" and desc != [] + grid( + columns: 2, + column-gutter: 1em, + text(weight: "bold", entry.short), + { + if hasLong { + text(weight: "bold", entry.long) } - ) - } + if hasLong and hasDesc [:] + if hasDesc [ #desc ] else [. ] + if disable-back-references != true { + term_references.map(x => x.location()).sorted(key: x => x.page()).fold( + (values: (), pages: ()), + ((values, pages), x) => if pages.contains(x.page()) { + (values: values, pages: pages) + } else { + values.push(x) + pages.push(x.page()) + (values: values, pages: pages) + }, + ).values.map(x => { + let page-numbering = x.page-numbering() + if page-numbering == none { + page-numbering = "1" + } + link(x)[#numbering( + page-numbering, + ..counter(page).at(x), + )] + }).join(", ") + } + }, + ) } - }, - )[] #label(entry.key) + } + }, + )[] #label(entry.key) #parbreak() ] } } - if enable-group-pagebreak { pagebreak(weak: true) } + if enable-group-pagebreak { + pagebreak(weak: true) + } } }; \ No newline at end of file diff --git a/src/glossary.typ b/src/glossary.typ index 3fb0aef..4a9555f 100644 --- a/src/glossary.typ +++ b/src/glossary.typ @@ -9,66 +9,72 @@ #let glossary(entries) = { + assert( + type(entries) == dictionary, + message: "The glossary is not a dictionary", + ) + + for (k, v) in entries.pairs() { assert( - type(entries) == dictionary, - message: "The glossary is not a dictionary", + type(v) == dictionary, + message: "The glossary entry `" + k + "` is not a dictionary", ) - for (k, v) in entries.pairs() { + for key in v.keys() { assert( - type(v) == dictionary, - message: "The glossary entry `" + k + "` is not a dictionary") - - for key in v.keys() { - assert( - key in ("short", "long", "desc", "group"), - message: "Found unexpected key `" + key + "` in glossary entry `" + k) - } - - assert( - type(v.short) == str, - message: "The short form of glossary entry `" + k + "` is not a string") - - if "long" in v { - assert( - type(v.long) == str, - message: "The long form of glossary entry `" + k + "` is not a string") - } - - if "desc" in v { - assert( - type(v.desc) == str, - message: "The description of glossary entry `" + k + "` is not a string") - } - - if "group" in v { - assert( - type(v.group) == str, - message: "The optional group of glossary entry `" + k + "` is not a string") - } else { - let group = if "long" in v { - if (context text.lang) == "de" { - "Akronyme" - } else { - "Acronyms" - } - } else { - if (context text.lang) == "de" { - "Begriffe" - } else { - "Terms" - } - } - - entries.at(k).group = group - } + key in ("short", "long", "desc", "group"), + message: "Found unexpected key `" + key + "` in glossary entry `" + k, + ) } + assert( + type(v.short) == str, + message: "The short form of glossary entry `" + k + "` is not a string", + ) + + if "long" in v { + assert( + type(v.long) == str, + message: "The long form of glossary entry `" + k + "` is not a string", + ) + } + + if "desc" in v { + assert( + type(v.desc) == str, + message: "The description of glossary entry `" + k + "` is not a string", + ) + } + + if "group" in v { + assert( + type(v.group) == str, + message: "The optional group of glossary entry `" + k + "` is not a string", + ) + } else { + let group = if "long" in v { + if (context text.lang) == "de" { + "Akronyme" + } else { + "Acronyms" + } + } else { + if (context text.lang) == "de" { + "Begriffe" + } else { + "Terms" + } + } + + entries.at(k).group = group + } + } + return entries.pairs().map(((key, entry)) => ( - key: key, - short: eval(entry.short, mode: "markup"), - long: eval(entry.at("long", default: ""), mode: "markup"), - desc: eval(entry.at("desc", default: ""), mode: "markup"), - group: entry.at("group", default: "") - )) + key: key, + short: eval(entry.short, mode: "markup"), + long: eval(entry.at("long", default: ""), mode: "markup"), + desc: eval(entry.at("desc", default: ""), mode: "markup"), + group: entry.at("group", default: ""), + )) } \ No newline at end of file diff --git a/src/lib.typ b/src/lib.typ index 905e383..f3098cd 100644 --- a/src/lib.typ +++ b/src/lib.typ @@ -7,20 +7,20 @@ // Edited: 27.06.2024 // License: MIT - #import "conf.typ": validate-config - #import "branding.typ": * - #import "style.typ": global_styled_doc, content_styled, end_styled - #import "glossary.typ": glossary - #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/preface.typ": new-preface - #import "pages/appendix.typ": show-appendix +#import "conf.typ": validate-config +#import "branding.typ": * +#import "style.typ": global_styled_doc, content_styled, end_styled +#import "glossary.typ": glossary +#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/preface.typ": new-preface +#import "pages/appendix.typ": show-appendix -#let group-break()= { +#let group-break() = { [#pagebreak()] } @@ -36,12 +36,14 @@ #set document( author: config.author.name, keywords: config.thesis.keywords, - title: config.thesis.title) + title: config.thesis.title, + ) // configure text locale #set text( lang: config.lang, - region: config.region) + region: config.region, + ) // preppend title page #new_title_page(config) @@ -57,7 +59,7 @@ #new-preface(config) #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 #import "glossarium.typ": * #show: make-glossary @@ -68,10 +70,11 @@ print-glossary( disable-back-references: true, enable-group-pagebreak: true, - glossary(config.thesis.glossary)) + glossary(config.thesis.glossary), + ) pagebreak(weak: true) - } + } #counter(page).update(1) // mark end of prelude diff --git a/src/pages/abstract.typ b/src/pages/abstract.typ index f2feb5a..67e73a3 100644 --- a/src/pages/abstract.typ +++ b/src/pages/abstract.typ @@ -7,7 +7,7 @@ // License: MIT #let new_abstract(config) = context { - + set align(center + horizon) // only include summary when a language other than english is used diff --git a/src/pages/outline.typ b/src/pages/outline.typ index 0811949..e0b3d8d 100644 --- a/src/pages/outline.typ +++ b/src/pages/outline.typ @@ -9,63 +9,65 @@ // render an outline of figures // with a specific title and filter by a specifc kind of figure // 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 elems = query(figure.where(kind: kind), here()) let count = elems.len() - + // only show outline if there is something to list if count > 0 { pagebreak(weak: true) outline( title: title, - target: figure.where(kind: kind)) + target: figure.where(kind: kind), + ) } } #let render_figures_outline() = context { let title = if (text.lang == "de") { - "Abbildungsverzeichnis" - } else if text.lang == "en" { - "List of Figures" - } + "Abbildungsverzeichnis" + } else if text.lang == "en" { + "List of Figures" + } render_filtered_outline(title: title, kind: image) } #let render_table_outline() = context { let title = if (text.lang == "de") { - "Tabellenverzeichnis" - } else if text.lang == "en" { - "List of Tables" - } + "Tabellenverzeichnis" + } else if text.lang == "en" { + "List of Tables" + } render_filtered_outline(title: title, kind: table) } #let render_raw_outline() = context { let title = if (text.lang == "de") { - "Quelltextverzeichnis" - } else if text.lang == "en" { - "Code Snippets" - } + "Quelltextverzeichnis" + } else if text.lang == "en" { + "Code Snippets" + } render_filtered_outline(title: title, kind: raw) } #let render_heading_outline() = context { let title = if (text.lang == "de") { - "Inhaltsverzeichnis" - } else if text.lang == "en" { - "Table of Contents" - } - + "Inhaltsverzeichnis" + } else if text.lang == "en" { + "Table of Contents" + } + pagebreak(weak: true) outline( - target: heading.where(supplement: [chapter]), - title: title, - indent: auto) + target: heading.where(supplement: [chapter]), + title: title, + indent: auto, + ) } #let render_appendix_outline() = context { @@ -81,21 +83,20 @@ } else if text.lang == "en" { "Table of Appendices" } - + pagebreak(weak: true) outline( - target: heading.where(supplement: supplement), - title: title, - indent: auto) + target: heading.where(supplement: supplement), + title: title, + indent: auto, + ) } } #let new_outline() = { pagebreak(weak: true) - show outline.entry.where( - level: 1, - ): it => { + show outline.entry.where(level: 1): it => { v(1.5em, weak: true) strong(it) } diff --git a/src/pages/titlepage.typ b/src/pages/titlepage.typ index d77268b..d289053 100644 --- a/src/pages/titlepage.typ +++ b/src/pages/titlepage.typ @@ -60,24 +60,14 @@ column-gutter: 1cm, align: left, stroke: none, - - [*Verfasser:*], - author.name, - - [*Bearbeitungszeitraum:*], - thesis.timeframe, - + [*Verfasser:*], author.name, + [*Bearbeitungszeitraum:*], thesis.timeframe, [*Matrikelnummer, Kurs:*], str(author.matriculation-number) + ", " + author.course, - [*Ausbildungsbetrieb:*], - author.company, - - [*Betrieblicher Betreuer:*], - author.supervisor, - - [*Abgabedatum:*], - thesis.submission-date + [*Ausbildungsbetrieb:*], author.company, + [*Betrieblicher Betreuer:*], author.supervisor, + [*Abgabedatum:*], thesis.submission-date, ) ] else if text.lang == "en" [ #table( @@ -85,30 +75,21 @@ column-gutter: 1cm, align: left, stroke: none, - - [*Author:*], - author.name, - - [*Editing period:*], - thesis.timeframe, - + [*Author:*], author.name, + [*Editing period:*], thesis.timeframe, [*Matriculation number, course:*], str(author.matriculation-number) + ", " + author.course, - [*Training company:*], - author.company, - - [*Company supervisor:*], - author.supervisor, - - [*Submission date:*], - thesis.submission-date + [*Training company:*], author.company, + [*Company supervisor:*], author.supervisor, + [*Submission date:*], thesis.submission-date, ) ] else [ #context panic("no translation for language: ", text.lang) ] - #align(bottom, + #align( + bottom, grid( // set width of columns // we need two, so make both half the page width @@ -121,7 +102,8 @@ #context panic("no translation for language: ", text.lang) ] ), - align(right, {line(length: 6cm)}))) + align(right, {line(length: 6cm)})), + ) #counter(page).update(0) ] diff --git a/src/style.typ b/src/style.typ index e8c6966..779e2ce 100644 --- a/src/style.typ +++ b/src/style.typ @@ -9,14 +9,15 @@ #import "branding.typ": * -#let watermark-color = luma(50%).transparentize(70%) +#let watermark-color = luma(50%).transparentize(70%) #let watermark(config) = if config.draft { rotate(-22.5deg)[ #rect( - radius: 1em, - inset: 1em, - stroke: watermark-color)[ + radius: 1em, + inset: 1em, + stroke: watermark-color, + )[ #text(size: 4em, weight: "bold", fill: watermark-color, "DRAFT") #linebreak() #text(size: 1.25em, weight: "bold", fill: watermark-color)[ @@ -24,7 +25,11 @@ #linebreak() document version. #linebreak() - #text(size: 0.75em, "Further usage without the authors consent is not permitted.")]]]} + #text( + size: 0.75em, + "Further usage without the authors consent is not permitted.", + )]]] +} // global style of document #let global_styled_doc(config, body) = { @@ -37,11 +42,13 @@ hyphenate: true, dir: ltr, font: style.text.font, - fill: ABB-BLACK) + fill: ABB-BLACK, + ) show heading: set text( font: style.heading.font, - weight: "semibold") + weight: "semibold", + ) set heading(supplement: [chapter]) @@ -55,7 +62,8 @@ // set theme for code blocks set raw( tab-size: style.code.tab-size, - theme: style.code.theme) + theme: style.code.theme, + ) show raw: set text(font: style.code.font) show figure: set block(breakable: true) @@ -67,8 +75,20 @@ stroke: (x, y) => ( left: none, right: none, - top: if y == 0 { 1.5pt } else if y < 2 { 1pt } else { 0pt }, - bottom: if y == 0 { 1pt } else { 1.5pt } )) + top: if y == 0 { + 1.5pt + } else if y < 2 { + 1pt + } else { + 0pt + }, + bottom: if y == 0 { + 1pt + } else { + 1.5pt + }, + ), + ) // make table header bold show table.cell.where(y: 0): set text(weight: "bold") @@ -77,7 +97,8 @@ set par( justify: true, first-line-indent: 1em, - leading: 1em) + leading: 1em, + ) // give links a color show link: set text(fill: style.link.color) @@ -93,10 +114,11 @@ top: style.page.margin.top + style.header.logo-height + style.header.underline-top-padding + style.header.content-padding, bottom: style.page.margin.bottom + style.footer.content-padding, left: style.page.margin.left, - right: style.page.margin.right), + right: style.page.margin.right, + ), numbering: (..nums) => { let current-page = here().page() - if current-page == 1{ + if current-page == 1 { [] } else if query().first().location().page() > current-page { numbering("I", nums.pos().first()) @@ -115,7 +137,11 @@ } else if query().first().location().page() > page-number { numbering("I", page-counter) } else if query().first().location().page() >= page-number { - numbering("1 / 1", page-counter, counter(page).at().last()) + numbering( + "1 / 1", + page-counter, + counter(page).at().last(), + ) } else { numbering("a", page-counter) } @@ -134,14 +160,18 @@ align(right, image("res/DHBW.svg", height: style.header.logo-height))) } else if query().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()), + ) let header-title = thesis.title if headers-before.len() > 0 { header-title = headers-before.last().body } else { - let headers-after = query(selector(heading.where(numbering: "1.", level: 1)).after(here())) + let headers-after = query( + selector(heading.where(numbering: "1.", level: 1)).after(here()), + ) if headers-after.len() > 0 { header-title = headers-after.first().body @@ -152,8 +182,9 @@ columns: (1fr, auto), align: (horizon, bottom), context [ _ #header-title _ ], - image("res/DHBW.svg", height: style.header.logo-height)) - + image("res/DHBW.svg", height: style.header.logo-height), + ) + v(style.header.underline-top-padding - 1em) line(length: 100%) } else { @@ -161,12 +192,13 @@ columns: (1fr, auto), align: (horizon, bottom), context [ _ #config.thesis.title _ ], - image("res/DHBW.svg", height: style.header.logo-height) + image("res/DHBW.svg", height: style.header.logo-height), ) v(style.header.underline-top-padding - 1em) line(length: 100%) } - }) + }, + ) body }