Compare commits

..

No commits in common. "main" and "v0.6.0" have entirely different histories.
main ... v0.6.0

25 changed files with 184 additions and 454 deletions

View File

@ -12,44 +12,27 @@ jobs:
env:
USERNAME: servostar
EMAIL: sven.vogel123@web.de
GIT_AUTH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
with:
ref: 'main'
token: ${{ secrets.RELEASE_TOKEN }}
- name: Install dependencies for Nix setup action
run: |
apt update -y
apt install sudo -y
- name: Setup Nix
uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Setup Git
run: |
git config --global user.name "${USERNAME}"
git config --global user.email "${EMAIL}"
- name: Update Typst.toml
- name: Tag Release Commit
env:
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
GIT_AUTH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
export VERSION=$(echo "${COMMIT_MESSAGE}" | grep -Po '(?<=Release-As: )v\d+\.\d+\.\d+')
git config --global user.name "${USERNAME}"
git config --global user.email "${EMAIL}"
echo "==> Update typst.toml version"
sed -i "/version/c\version = \"${VERSION#v}\"" typst.toml
git add typst.toml
git commit -m "chore: bump release version to $VERSION" -m "Generated-By: ${{ gitea.actor }}"
- name: Generate Example Document
run: nix-shell --run ./run-ci.sh
- name: Generate Assets
if: contains(github.event.head_commit.message, 'Generate-Assets')
run: |
./run-bake-assets.sh
git add assets/banner.png
git add assets/page-preview.png
git commit -m "chore: update assets" -m "Generated-By: ${{ gitea.actor }}"
- name: Tag and Push Changes
run: |
git push origin main
export VERSION=$(echo "${COMMIT_MESSAGE}" | grep -Po '(?<=Release-As: )v\d+\.\d+\.\d+')
echo "::endgroup::"
echo "::group::{Tag commit}"
git tag -m "Release" "${VERSION}"
git push origin "${VERSION}"
echo "::endgroup::"

View File

@ -24,7 +24,7 @@ jobs:
nix-shell --run ./run-ci.sh
- name: Upload artifact
id: artifact-upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: example-document
path: ${{ github.workspace }}/example.pdf
@ -47,7 +47,7 @@ jobs:
go-version: '>=1.20.1'
- name: Download Artifcat
id: download
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: example-document
path: ${{ github.workspace }}/Example.pdf

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
*.pdf
*.log
images/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@ -1,59 +0,0 @@
#import "@preview/shadowed:0.1.2": shadowed
#let LE = 10cm
#let shadow = 0.25cm
#set page(margin: 0pt, width: 2 * LE, height: LE, fill: rgb(0, 0, 0, 0))
#place(
dx: 3cm,
dy: 1.1cm,
rotate(
-9deg,
shadowed(
radius: 0pt,
inset: 0pt,
blur: shadow,
image("./images/21.svg", height: LE * 0.6))))
#place(
dx: 1cm,
dy: 3.25cm,
rotate(
-20deg,
shadowed(
radius: 0pt,
inset: 0pt,
blur: shadow,
image("./images/51.svg", height: LE * 0.55))))
#place(
dx: 14cm,
dy: 1.5cm,
rotate(
6deg,
shadowed(
radius: 0pt,
inset: 0pt,
blur: shadow,
image("./images/41.svg", height: LE * 0.65))))
#place(
dx: 11cm,
dy: 2cm,
rotate(
25deg,
shadowed(
radius: 0pt,
inset: 0pt,
blur: shadow,
image("./images/31.svg", height: LE * 0.6))))
#place(
dx: 7cm,
dy: 0.75cm,
shadowed(
radius: 0pt,
inset: 0pt,
blur: shadow,
image("./images/11.svg", height: LE * 0.75)))

View File

@ -1,34 +0,0 @@
#let gap = 0.5cm
#set page(margin: 0pt, width: 210mm * 3 + 2 * gap, height: 297mm * 2 + gap, fill: rgb(0, 0, 0, 0))
#place(
dx: 0mm,
dy: 0mm,
image("./images/11.svg", height: 297mm))
#place(
dx: 210mm + gap,
dy: 0mm,
image("./images/21.svg", height: 297mm))
#place(
dx: (210mm + gap) * 2,
dy: 0mm,
image("./images/31.svg", height: 297mm))
#place(
dx: 210mm * 0,
dy: 297mm + gap,
image("./images/71.svg", height: 297mm))
#place(
dx: (210mm + gap) * 1,
dy: 297mm + gap,
image("./images/41.svg", height: 297mm))
#place(
dx: (210mm + gap) * 2,
dy: 297mm + gap,
image("./images/51.svg", height: 297mm))

View File

@ -1,18 +0,0 @@
#!/bin/bash
echo "==> generate images"
mkdir images || true
# extract images from document created by action
mutool convert -o images/1.svg "$1" 1
mutool convert -o images/2.svg "$1" 2
mutool convert -o images/2.svg "$1" 2
mutool convert -o images/3.svg "$1" 5
mutool convert -o images/7.svg "$1" 7
mutool convert -o images/4.svg "$1" 15
mutool convert -o images/5.svg "$1" 16
echo "==> generate banner"
typst compile banner.typ --ppi 360 ../assets/banner.png
echo "==> generate preview"
typst compile preview.typ --ppi 360 ../assets/page-preview.png

View File

@ -1,14 +0,0 @@
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-unstable";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
pkgs.mkShellNoCC {
packages = with pkgs; [
python312
python312Packages.pyyaml
typst
typstyle
mupdf-headless
];
}

View File

@ -1,6 +0,0 @@
#!/bin/bash
cd baker
echo "==> generate assets"
nix-shell --run "./run-bake-banner.sh ../example.pdf"

View File

@ -17,7 +17,7 @@ function format() {
exit 1
fi
local imports=$(rg "#import \"([a-z0-9/\-.]+\.typ)\"" -Nor '$1' "$1")
local imports=$(rg "#import \"([a-z0-9/\-]+\.typ)\"" -Nor '$1' "$1")
# format all included files
while IFS= read -r line; do
@ -26,16 +26,6 @@ function format() {
fi
format "$wd/$line" "$2"
done <<< "$imports"
local includes=$(rg "#import \"([a-z0-9/\-.]+\.typ)\"" -Nor '$1' "$1")
# format all included files
while IFS= read -r line; do
if [ -z "$line" ]; then
continue
fi
format "$wd/$line" "$2"
done <<< "$includes"
}
case $1 in

View File

@ -1,5 +1,5 @@
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-unstable";
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.11";
pkgs = import nixpkgs { config = {}; overlays = []; };
in

View File

@ -11,78 +11,64 @@
// default configuration
#let default-config = (
// language settings used to make decisions about hyphenation and others
lang: "en", // ISO 3166 language code of text: "de", "en"
region: "en", // region code
// mark this thesis as draft
// Adds preleminarry note page and watermark
draft: true,
// information about author(s)
university: (
program: "Informationtechnology",
faculty: "Technik",
name: "dualen Hochschule Baden-Württemberg",
campus: "Eppelheim",
),
supervisor-signature: false,
authors: ((), ()),
coauthors: (),
// information about thesis
thesis: (
title: "Unofficial ABB/DHBW Typst template",
subtitle: "for reports and thesises", // subtitle may be none
submission-date: "23rd march 2020",
timeframe: "1st january 2020 - 20th march 2020",
kind: "T2000",
// translated version of abstract, only used in case language is not english
summary: none,
abstract: none,
preface: none,
keywords: ("IT", "other stuff"),
bibliography: none, /* bibliography("refs.bib") */
glossary: none,
appendices: none,
confidentiality: true,
authorship: true,
),
style: (
header: (
content-padding: 1.5em,
underline-top-padding: 0pt,
logo-height: 5em,
logo-image: "res/ABB.svg",
// language settings used to make decisions about hyphenation and others
lang: "en", // ISO 3166 language code of text: "de", "en"
region: "en", // region code
// mark this thesis as draft
// Adds preleminarry note page and watermark
draft: true,
// information about author(s)
university: (
program: "Informationtechnology",
faculty: "Technik",
name: "dualen Hochschule Baden-Württemberg",
campus: "Eppelheim"
),
footer: (
content-padding: 1.5em,
),
page: (
format: "a4",
margin: (
left: 3cm,
right: 2.5cm,
top: 2.5cm,
bottom: 2.5cm,
),
),
text: (
size: 12pt,
font: "Fira Sans",
),
heading: (
font: "Fira Sans",
),
code: (
theme: "res/abb.tmTheme",
font: "FiraCode Nerd Font",
lines: false,
size: 10pt,
tab-size: 4,
),
link: (
color: ABB-GRAY-02,
),
),
)
supervisor-signature: false,
authors: ((),()),
// information about thesis
thesis: (
title: "Unofficial ABB/DHBW Typst template",
subtitle: "for reports and thesises", // subtitle may be none
submission-date: "23rd march 2020",
timeframe: "1st january 2020 - 20th march 2020",
kind: "T2000",
// translated version of abstract, only used in case language is not english
summary: none,
abstract: none,
preface: none,
keywords: ( "IT", "other stuff" ),
bibliography: none /* bibliography("refs.bib") */,
glossary: none,
appendices: none),
style: (
header: (
content-padding: 1.5em,
underline-top-padding: 0pt,
logo-height: 5em,
logo-image: "res/ABB.svg"),
footer: (
content-padding: 1.5em),
page: (
format: "a4",
margin: (
left: 3cm,
right: 2.5cm,
top: 2.5cm,
bottom: 2.5cm)),
text: (
size: 12pt,
font: "Fira Sans"),
heading: (
font: "Fira Sans"),
code: (
theme: "res/abb.tmTheme",
font: "FiraCode Nerd Font",
lines: false,
size: 10pt,
tab-size: 4),
link: (
color: ABB-GRAY-02)))
// Insert a dictionary `update` into `base` but only the entries of update that also exist in base
// Runs recursively on all sub dictionaries
@ -107,15 +93,7 @@
} 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)
+ "`",
)
panic("missmatched dictionary entry `" + key + "` type: expected `" + type(val) + "` got `" + type(update_val) + "`")
}
} else {
base.insert(key, val)

View File

@ -59,13 +59,8 @@ SOFTWARE.*/
let textLink = if display != none {
[#display]
} else if (
(
is_first or long == true
)
and entlong != []
and entlong != ""
and long != false
) {
is_first or long == true
) and entlong != [] and entlong != "" and long != false {
[#entlong (#entry.short#suffix)]
} else {
[#entry.short#suffix]
@ -113,13 +108,8 @@ SOFTWARE.*/
}
let textLink = if (
(
is_first or long == true
)
and entlong != []
and entlong != ""
and long != false
) {
is_first or long == true
) and entlong != [] and entlong != "" and long != false {
[#entlong (#short)]
} else {
[#short]
@ -138,11 +128,9 @@ SOFTWARE.*/
// show rule to make the references for glossarium
#let make-glossary(body) = {
show ref: r => {
if (
r.element != none
and r.element.func() == figure
and r.element.kind == __glossarium_figure
) {
if r.element != none and r.element.func() == figure and r
.element
.kind == __glossarium_figure {
// call to the general citing function
gls(str(r.target), suffix: r.citation.supplement)
} else {
@ -209,7 +197,6 @@ SOFTWARE.*/
#let hasLong = long != "" and long != []
#let hasDesc = desc != "" and desc != []
#set align(left)
#block(
below: 1.5em,
width: 100%,
@ -242,15 +229,16 @@ SOFTWARE.*/
)
.values
.map(x => {
let page-numbering = x.page-numbering()
if page-numbering == none {
page-numbering = "1"
let page-numbering = x.page-numbering()
if page-numbering == none {
page-numbering = "1"
}
link(x)[#numbering(
page-numbering,
..counter(page).at(x),
)]
}
link(x)[#numbering(
page-numbering,
..counter(page).at(x),
)]
})
)
.join(", ")
}
],

View File

@ -8,6 +8,7 @@
// License: MIT
#let glossary(entries, config) = {
assert(
type(entries) == dictionary,
message: "The glossary is not a dictionary",
@ -43,18 +44,14 @@
if "desc" in v {
assert(
type(v.desc) == str,
message: "The description of glossary entry `"
+ k
+ "` is not a string",
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",
message: "The optional group of glossary entry `" + k + "` is not a string",
)
} else {
let acronym_group = if config.lang == "de" {
@ -93,13 +90,11 @@
}
}
return processed_glossary
.pairs()
.map(((key, entry)) => (
key: key,
short: entry.short,
long: eval(entry.at("long", default: ""), mode: "markup"),
desc: eval(entry.at("desc", default: ""), mode: "markup"),
group: entry.at("group", default: ""),
))
return processed_glossary.pairs().map(((key, entry)) => (
key: key,
short: entry.short,
long: eval(entry.at("long", default: ""), mode: "markup"),
desc: eval(entry.at("desc", default: ""), mode: "markup"),
group: entry.at("group", default: ""),
))
}

View File

@ -126,9 +126,7 @@
config,
context [
// add bibliography if set
#if (
"bibliography" in config.thesis and config.thesis.bibliography != none
) {
#if "bibliography" in config.thesis and config.thesis.bibliography != none {
pagebreak(weak: true)
counter(page).update(1)
set bibliography(

View File

@ -8,6 +8,7 @@
#let new_abstract(config) = (
context {
set align(center + horizon)
// only include summary when a language other than english is used

View File

@ -8,9 +8,6 @@
#let new_confidentiality_statement_page(config) = (
context {
if not config.thesis.confidentiality {
return
}
pagebreak(weak: true)
@ -54,24 +51,13 @@
set align(horizon)
grid(
// set width of columns
// we need two, so make both half the page width
columns: (50%, 50%),
row-gutter: 0.75em,
align(left, { line(length: 6cm) }),
align(left, { line(length: 6cm) }),
align(
left,
if text.lang == "de" [ Ort, Datum ] else if text.lang == "en" [
Place, Date
] else { panic("no translation for language: ", text.lang) },
),
align(
left,
if text.lang == "de" [ Unterschrift ] else if text.lang == "en" [
Signature
] else { panic("no translation for language: ", text.lang) },
)
)
// set width of columns
// we need two, so make both half the page width
columns: (50%, 50%),
row-gutter: 0.75em,
align(left, {line(length: 6cm)}),
align(left, {line(length: 6cm)}),
align(left, if text.lang == "de" [ Ort, Datum ] else if text.lang == "en" [ Place, Date ] else { panic("no translation for language: ", text.lang) }),
align(left, if text.lang == "de" [ Unterschrift ] else if text.lang == "en" [ Signature ] else { panic("no translation for language: ", text.lang) }))
}
)

View File

@ -8,9 +8,6 @@
#let new_declaration_of_authorship(config) = (
context {
if not config.thesis.authorship {
return
}
pagebreak(weak: true)
@ -56,24 +53,13 @@
set align(horizon)
grid(
// set width of columns
// we need two, so make both half the page width
columns: (50%, 50%),
row-gutter: 0.75em,
align(left, { line(length: 6cm) }),
align(left, { line(length: 6cm) }),
align(
left,
if text.lang == "de" [ Ort, Datum ] else if text.lang == "en" [
Place, Date
] else { panic("no translation for language: ", text.lang) },
),
align(
left,
if text.lang == "de" [ Unterschrift ] else if text.lang == "en" [
Signature
] else { panic("no translation for language: ", text.lang) },
)
)
// set width of columns
// we need two, so make both half the page width
columns: (50%, 50%),
row-gutter: 0.75em,
align(left, {line(length: 6cm)}),
align(left, {line(length: 6cm)}),
align(left, if text.lang == "de" [ Ort, Datum ] else if text.lang == "en" [ Place, Date ] else { panic("no translation for language: ", text.lang) }),
align(left, if text.lang == "de" [ Unterschrift ] else if text.lang == "en" [ Signature ] else { panic("no translation for language: ", text.lang) }))
}
)

View File

@ -12,18 +12,16 @@
// 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))
let count = elems.len()
show outline.entry: it => {
link(it.element.location())[
#v(12pt, weak: true)
#it.prefix()
#[:]
#h(0.5em)
#text(weight: "regular", it.body())
#text(weight: "regular", it.body)
#box(width: 1fr, it.fill)
#[ #it.page()]
#[ #it.page]
]
}
@ -125,14 +123,9 @@
#let new_outline() = {
pagebreak(weak: true)
show outline.entry: it => {
if it.level == 1 {
v(1.5em, weak: true)
strong(it)
} else {
v(1.0em, weak: true)
it
}
show outline.entry.where(level: 1): it => {
v(1.5em, weak: true)
strong(it)
}
render_heading_outline()

View File

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

View File

@ -21,27 +21,14 @@
if config.style.header.logo-image == none {
// error
} else if config.style.header.logo-image.len() > 0 {
align(
left,
image(
config.style.header.logo-image,
height: config.style.header.logo-height,
),
)
align(left, image(config.style.header.logo-image, height: config.style.header.logo-height))
} else {
align(
left,
image("../res/DHBW.svg", height: config.style.header.logo-height),
)
align(left, image("../res/DHBW.svg", height: config.style.header.logo-height))
},
// right align logo of DHBW
if config.style.header.logo-image.len() > 0 {
align(
right,
image("../res/DHBW.svg", height: config.style.header.logo-height),
)
}
)
align(right, image("../res/DHBW.svg", height: config.style.header.logo-height))
})
#set align(center)
@ -112,7 +99,7 @@
#v(1.5em)
#let rows = int(config.authors.len() / 3 + 0.9)
#let rows = int(config.authors.len() / 3 + 0.5)
#for i in range(0, rows) {
let cols = calc.min(config.authors.len() - i * 3, 3)
@ -120,31 +107,17 @@
grid(columns: cols, column-gutter: 1.5em, ..config
.authors
.slice(i * 3, i * 3 + cols)
.map(author => par([
#if author.at("name", default: none) != none {
text(size: 1.25em, author.name)
linebreak()
}
#if author.at("company", default: none) != none {
text(size: 1em, author.company)
linebreak()
}
#if author.at("contact", default: none) != none {
text(size: 1em, author.contact)
linebreak()
}
#str(author.matriculation-number), #author.course
])))
}
]
#v(1em)
#align(center)[
coauthored in part by
#linebreak()
#for author in config.coauthors {
author
.map(author => grid(
columns: 1,
row-gutter: 1em,
text(size: 1.25em, author.name),
text(size: 1em, author.company),
text(size: 1em, author.contact),
[
#str(author.matriculation-number),
#author.course
],
)))
}
]
@ -158,21 +131,18 @@
align(
bottom,
grid(
// set width of columns
// we need two, so make both half the page width
columns: (60%, 40%),
align(
left,
if text.lang == "de" [
Unterschrift des betrieblichen Betreuers
] else if text.lang == "en" [
Signature of the company supervisor
] else [
#context panic("no translation for language: ", text.lang)
],
),
align(right, { line(length: 6cm) })
// set width of columns
// we need two, so make both half the page width
columns: (60%, 40%),
align(left, if text.lang == "de" [
Unterschrift des betrieblichen Betreuers
] else if text.lang == "en" [
Signature of the company supervisor
] else [
#context panic("no translation for language: ", text.lang)
]
),
align(right, {line(length: 6cm)})),
)
}

View File

@ -166,10 +166,10 @@
.map(make_row)
.flatten()
.map(c => if c.has("text") and c.text == "" {
v(1em)
} else {
c
})
v(1em)
} else {
c
})
)
},
)
@ -193,15 +193,13 @@
// change the display supplement according to the text langugae
// based on: https://github.com/typst/typst/issues/3273
show figure.where(kind: raw): set figure(
supplement: context {
if text.lang == "de" {
"Quelltext"
} else {
"Listing"
}
},
)
show figure.where(kind: raw): set figure(supplement: context {
if text.lang == "de" {
"Quelltext"
} else {
"Listing"
}
})
// APA style table
set table(
@ -243,13 +241,12 @@
set page(
paper: style.page.format,
foreground: watermark(config),
background: [#rect(width: 100%, height: 100%, fill: white)],
header-ascent: style.header.content-padding,
footer-descent: style.header.content-padding,
margin: (
top: style.page.margin.top
+ style.header.underline-top-padding
+ style.header.content-padding,
top: style.page.margin.top + 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,
@ -258,13 +255,15 @@
let current-page = here().page()
if current-page == 1 {
[]
} else if (
query(<end-of-prelude>).first().location().page() > current-page
) {
} else if query(<end-of-prelude>)
.first()
.location()
.page() > current-page {
numbering("I", nums.pos().first())
} else if (
query(<end-of-content>).first().location().page() >= current-page
) {
} else if query(<end-of-content>)
.first()
.location()
.page() >= current-page {
numbering("1", nums.pos().first())
} else {
numbering("a", nums.pos().first())
@ -277,14 +276,10 @@
#if page-number == 1 {
[]
} else if (
query(<end-of-prelude>).first().location().page() > page-number
) {
} else if query(<end-of-prelude>).first().location().page() > page-number {
set align(center)
numbering("I", page-counter)
} else if (
query(<end-of-content>).first().location().page() >= page-number
) {
} else if query(<end-of-content>).first().location().page() >= page-number {
numbering(
"1 / 1",
page-counter,
@ -300,11 +295,14 @@
if current-page == 1 {
// logo moved to content
} else if (
query(<end-of-content>).first().location().page() >= current-page
and query(<end-of-prelude>).first().location().page()
< current-page + 1
) {
} else if query(<end-of-content>)
.first()
.location()
.page() >= current-page and query(<end-of-prelude>)
.first()
.location()
.page() < current-page + 1 {
let heading = currentH()
heading.at(0)

View File

@ -24,9 +24,6 @@
contact: "sven.vogel123@web.de"
)
),
coauthors: (
"Gorbatschew"
),
thesis: (
title: "Unofficial ABB/DHBW Typst template",
subtitle: "for reports and thesises",
@ -40,8 +37,6 @@
bibliography: bibliography("refs.bib"),
glossary: yaml("glossary.yml"),
appendices: include "appendix.typ",
confidentiality: false,
authorship: false
),
style: (
header: (
@ -50,7 +45,7 @@
)
))
#import "@preview/wrap-it:0.1.1": wrap-content
#import "@preview/wrap-it:0.1.0": wrap-content
= Lorem Ipsum

View File

@ -1,6 +1,6 @@
[package]
name = "dhbw-abb-typst-template"
version = "0.6.3"
version = "0.6.0"
entrypoint = "src/template.typ"
authors = ["Sven Vogel <sven.vogel1@de.abb.com>"]
license = "MIT"