initial commit: add simple obx report

This commit is contained in:
2025-10-16 08:46:22 +02:00
commit f84fef1424
4 changed files with 337 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

228
obx-report/0.1.0/lib.typ Normal file
View File

@@ -0,0 +1,228 @@
#import "@preview/hydra:0.6.2": hydra
#import "titlepage.typ": *
// ----- Main Template Function: `basic-report` ----------------------
#let basic-report(
doc-category: none,
doc-title: none,
author: none,
affiliation: none,
logo: none,
language: "de",
show-outline: true,
compact-mode: false,
heading-color: blue,
heading-font: "Ubuntu", // recommended alternatives: "Fira Sans", "Lato", "Source Sans Pro"
body,
) = {
// ----- Global Parameters ------------------------
set document(title: doc-title, author: author)
set text(lang: language)
let body-font = "Vollkorn"
let body-size = 11pt
// let heading-font = "Ubuntu"
// heading font is used in this size for kind of "information blocks"
let info-size = 10pt
// heading font is used in this size for different sorts of labels
let label-size = 9pt
// are we inside or outside of the outline (for roman/arabic page numbers)?
let in-outline = state("in-outline", if compact-mode {false} else {true})
let logox = if logo == none {
image("Oceanbox-logo-plain.png", width: 2cm)
} else {
logo
}
// ----- Title Page ------------------------
if (not compact-mode) {
counter(page).update(0) // so TOC after titlepage begins with page no 1 (roman)
titlepage(
doc-category,
doc-title,
author,
affiliation,
logox,
heading-font,
heading-color,
info-size,
)
}
// ----- Basic Text- and Page-Setup ------------------------
set text(
font: body-font,
size: body-size,
// Vollkorn has a broader stroke than other fonts; in order to adapt the grey value (Grauwert)
// of the page the font gets printed in a dark grey (instead of completely black)
fill: luma(50)
)
set par(
justify: true,
leading: 0.75em,
spacing: 1.65em,
first-line-indent: 0em,
)
// Page Grid:
// Horizontal 1.5cm-grid = 14u: 3u left margin, 9u text, 2u right margin
// Idea: one-sided document; if printed on paper, the pages are often bound or stapled
// on the left side; so more space needed on the left. On-screen it doesn't matter.
// Vertical 1.5cm-grid ≈ 20u: 2u top margin, 14u text, 2u botttom margin
// header with height ≈ 0.6cm is visually part of text block --> top margin = 3cm + 0.6cm
set page( // standard page with header
paper: "a4",
margin: (top: 3.6cm, left: 4.5cm, right: 3cm, bottom: 3cm),
// the header shows the main chapter heading on the left and the page number on the right
header: context {
if compact-mode and (counter(page).get().first() == 1) {
none
} else {
grid(
columns: (1fr, 1fr),
align: (left, right),
row-gutter: 0.5em,
text(font: heading-font, size: label-size,
context {hydra(1, use-last: false, skip-starting: false)},),
text(font: heading-font, size: label-size,
number-type: "lining",
context {if in-outline.get() {
counter(page).display("i") // roman page numbers for the TOC
} else {
counter(page).display("1") // arabic page numbers for the rest of the document
}
}
),
grid.cell(colspan: 2, line(length: 100%, stroke: 0.5pt)),
)
}
},
header-ascent: 1.5em
)
// ----- Headings & Numbering Schemes ------------------------
set heading(numbering: "1.")
show heading: set text(font: heading-font, fill: heading-color,
weight: if compact-mode {"bold"} else {"regular"})
show heading: set par(justify: false)
show heading.where(level: 1): it => {
v(3.8 * body-size, weak: true) + text(it) + v(0.2 * body-size)
}
show heading.where(level: 2): it => {
v(0.8 * body-size) + text(it) + v(0.2 * body-size)
}
show heading.where(level: 3): it => {
v(0.8 * body-size) + text(it) + v(0.2 * body-size)
}
set figure(numbering: "1")
show figure.caption: it => {
set text(font: heading-font, size: label-size)
block(it)
}
// ----- Table of Contents ------------------------
// to detect, if inside or outside the outline (for different page numbers)
show outline: it => {
in-outline.update(true)
it
in-outline.update(false)
}
// top-level TOC entries in bold without filling
show outline.entry.where(level: 1): it => {
set block(above: 2 * body-size)
set text(font: heading-font, weight: "bold", size: info-size)
link(
it.element.location(), // make entry linkable
it.indented(it.prefix(), it.body() + box(width: 1fr,) + strong(it.page()))
)
}
// other TOC entries in regular with adapted filling
show outline.entry.where(level: 2).or(outline.entry.where(level: 3)): it => {
set block(above: 0.8 * body-size)
set text(font: heading-font, size: info-size)
link(
it.element.location(), // make entry linkable
it.indented(
it.prefix(),
it.body() + " " +
box(width: 1fr, repeat([.], gap: 2pt)) +
" " + it.page()
)
)
}
if (show-outline and not compact-mode) {
outline(
title: if language == "de" {
"Inhalt"
} else if language == "fr" {
"Table des matières"
} else if language == "es" {
"Contenido"
} else if language == "it" {
"Indice"
} else if language == "nl" {
"Inhoud"
} else if language == "pt" {
"Índice"
} else if language == "zh" {
"目录"
} else if language == "ja" {
"目次"
} else if language == "ru" {
"Содержание"
} else if language == "ar" {
"المحتويات"
} else {
"Contents"
},
indent: auto,
)
counter(page).update(0) // so the first chapter starts at page 1 (now in arabic numbers)
} else {
in-outline.update(false) // even if outline is not shown, we want to continue with arabic page numbers
counter(page).update(1)
}
if (not compact-mode) {
pagebreak()
}
// ----- Body Text ------------------------
if compact-mode { // compact title infos in compact-mode
compact-title(
doc-category,
doc-title,
author,
affiliation,
logox,
heading-font,
heading-color,
info-size,
body-size,
label-size,
)
}
body
}

View File

@@ -0,0 +1,97 @@
// ===== Functions for TITLE PAGE and COMPACT TITLE ===============
// ===== TITLE PAGE: `titlepage` ====================
#let titlepage(
doc-category,
doc-title,
author,
affiliation,
logo,
heading-font, // the heading-font is also used for all text on the titlepage
heading-color, // heading-color applies as well for the title
info-size, // used throughout the document for "info text"
) = {
// ----- Page-Setup ------------------------
set page(
paper: "a4",
margin: (top: 3cm, left: 4.5cm, right: 3cm, bottom: 4.5cm),
)
// Some basic rules for the title page layout:
// - logo is right-justified
// - all other elements are left-justified
// - the page uses a grid of 1.5 cm units
// ----- Logo ------------------------
place(top + right, // `place` so that the remaining layout is independent of the size of the logo
logo,
)
v(6cm) // = 4 x 1.5 cm
// ----- Title Category & Title ------------------------
align(
left, // 1 x 14pt + 2 x 36pt ≈ 2 x 1.5 cm
text(font: heading-font, weight: "regular", size: 14pt,
doc-category),
)
text(font: heading-font, weight: "light", size: 36pt, fill: heading-color,
doc-title,
)
// ----- Info Block ------------------------
set par(leading: 1em)
place(
bottom + left,
text(
font: heading-font, weight: "regular", size: info-size, fill: black,
datetime.today().display("[day].[month].[year]") + str("\n") +
author + str("\n") +
affiliation),
)
}
// ===== COMPACT TITLE: `compact-title` ====================
#let compact-title(
doc-category,
doc-title,
author,
affiliation,
logo,
heading-font, // the heading-font is also used for all text on the titlepage
heading-color, // heading-color applies as well for the title
info-size, // used throughout the document for "info text"
body-size,
label-size,
) = {
// `place` so that the remaining layout is independent of the size of the logo
place(top + right, logo)
stack(
v(3.0cm),
box(height: 1.5cm,
text(font: heading-font, size: 1 * body-size,
top-edge: "ascender",
doc-category) ),
box(height: 4cm,
par(leading: 0.5em,
text(font: heading-font, weight: "bold",
size: 2 * body-size, fill: luma(40%).mix(heading-color),
top-edge: "ascender",
hyphenate: false,
doc-title) + "\n\n") +
text(font: heading-font, size: label-size,
author + "\n" +
affiliation + ", " +
datetime.today().display("[day].[month].[year]")
)
),
)
}

View File

@@ -0,0 +1,12 @@
[package]
name = "obx-report"
version = "0.1.0"
entrypoint = "lib.typ"
authors = ["Jonas Juselius and Roland Schätzle <@roland-KA>"]
license = "MIT"
description = "A simple template for reports"
repository = "https://gitlab.com/oceanbox/typst/obx-report"
keywords = ["report", "non-fiction", "basic"]
categories = ["paper", "report"]
exclude = ["template/main.pdf", "template/mainCompact.pdf"]