typst-chemex/chemex.typ

273 lines
6.1 KiB
Typst
Raw Permalink Normal View History

2025-11-09 12:56:49 +01:00
#import "@preview/typsium-ghs:0.1.0": *
#import "@preview/one-liner:0.2.0": fit-to-width
#import "@preview/subpar:0.2.2"
2025-11-09 21:46:12 +01:00
#import "@preview/typsium:0.3.0":*
2025-11-09 12:56:49 +01:00
2025-11-09 21:46:12 +01:00
#let imageradius = 2pt
#let explanation = [
Cats react with Mice to #ce("[Cu(H2O)4]^2+ + 4NH3 -> [Cu(NH3)4]^2+ + 4H2O")
]
#let language = "de"
#set text(lang:language)
2025-11-09 12:56:49 +01:00
#let repeated-table(num-repeats: 2, ..args) = {
let options = args.named()
let data = args.pos()
// STEP 1: transform table options to apply to a multiple of the original columns
let columns = options.at("columns", default: ())
let (column-count, columns) = if type(columns) == int {
// for numbers, that's number of columns
(columns, columns * num-repeats)
} else if type(columns) == array and columns != () {
// for arrays with elements, the number of elements is number of columns
(columns.len(), columns * num-repeats)
} else {
// lengths, auto or an empty array mean there's one column
(1, (auto,) * num-repeats)
}
options.columns = columns
// TODO transform other per-column fields, such as align
// STEP 2: separate header and footer from the table body, with repeated cells
let header = if data.len() > 0 and type(data.first()) == content and data.first().func() == table.header {
let (children, ..args) = data.remove(0).fields()
table.header(..args, ..children * num-repeats)
}
let footer = if data.len() > 0 and type(data.last()) == content and data.last().func() == table.footer {
let (children, ..args) = data.pop().fields()
table.footer(..args, ..children * num-repeats)
}
// STEP 3: rearrange the data, so that after a number of rows the next repetition begins
// split into rows
let rows = data.chunks(column-count)
// split into repeats of rows
let num-rows = calc.ceil(rows.len() / num-repeats)
let repeats = rows.chunks(num-rows)
// pad the last repeat so that all have the same number of rows
let empty-row = (none,) * column-count
repeats.last() += (empty-row,) * (num-rows - repeats.last().len())
// join repeats into combined rows
let rows = array.zip(..repeats)
// combine into flat data
data = rows.flatten()
// STEP 4: re-add header and footer to the data
if header != none {
data.insert(0, header)
}
if footer != none {
data.push(footer)
}
// STEP 5: produce table
table(..options, ..data)
}
#let my-subfig(..args) = subpar.grid(
show-sub-caption: (num, it) => {
set text(size: 7pt)
text(weight: "bold", num)
it.body
},
..args
)
// Define your image paths
#let image_paths = (
"figures/dummy.jpg",
"figures/dummy.jpg",
)
#let step_image_paths = (
"figures/dummy.jpg",
2025-11-09 21:46:12 +01:00
"none",
2025-11-09 12:56:49 +01:00
"figures/dummy.jpg",
)
2025-11-09 21:46:12 +01:00
#let safety_notices = (h-statement(310), h-statement(310))
2025-11-09 12:56:49 +01:00
#let step_descriptions = (
"Essen sie alle Kinder",
"Essen sie alle Fische",
2025-11-09 21:46:12 +01:00
lorem(10),
2025-11-09 12:56:49 +01:00
)
// NOTE: For Linebreaks, use \n in the text
#let amounts = ("500g", 2, 7, 6, 7, 2, 2, 7, 6, 7, 2)
#let material_names = ("Heißkleber", "Watteschen", "Plastic", "Wattestäbchen", "Wattestäbchen", "Watteschen", "Plastic", "Wattestäbchen", "Wattestäbchen","Heißkleber","Heißkleber","Heißkleber","Heißkleber","Heißkleber") // user input
2025-11-09 21:46:12 +01:00
#let tablecolumns = calc.ceil(amounts.len() / 7)
2025-11-09 12:56:49 +01:00
#let materials = material_names.map(name => text(name))
#let has_images = step_image_paths.any(path => path != "" and path != "none")
#let step_image_size = 4cm
#let steps = step_descriptions.enumerate().map(((i, desc)) => {
let img = if i < step_image_paths.len() { step_image_paths.at(i) } else { "none" }
grid(
2025-11-09 21:46:12 +01:00
columns: if has_images { (1fr, step_image_size) } else { (1fr, auto) },
align: (horizon+left, horizon+center),
inset: 5pt,
stroke: (
top: (paint: black, thickness: 0.1pt, dash: "dashed"),
bottom: (paint: black, thickness: 0.1pt, dash: "dashed"),
left: none,
right: none,
),
pad(top: 0.2cm, bottom: 0.2cm, text("Schritt " + str(i+1) + ":\n" + desc)),
2025-11-09 12:56:49 +01:00
if img != "none" {
2025-11-09 21:46:12 +01:00
pad(right: 10pt, block(
clip: true,
radius: imageradius,
image(img)
))
2025-11-09 12:56:49 +01:00
} else {
none
},
)
})
2025-11-09 21:46:12 +01:00
#let figures = image_paths.map(path => block(
clip: true,
radius: imageradius,
image(path)
))
2025-11-09 12:56:49 +01:00
//title and hazard
2025-11-09 21:46:12 +01:00
2025-11-09 12:56:49 +01:00
#grid(
columns: (0.7fr, 1fr),
align: (horizon, right+horizon),
2025-11-09 21:46:12 +01:00
stroke: (bottom:0.5pt + black),
2025-11-09 12:56:49 +01:00
inset: (5pt),
par(
text("Methan-Mamba", size: 22pt)),
grid(
columns: 9,
rows: 30pt,
..range(1,10).map(x=> ghs(x))
)
)
2025-11-09 21:46:12 +01:00
//safety
#v(-0.3cm)
#show table.cell: set text(size: 6pt)
#box( stroke: 0.5pt + red, inset: -1pt, radius: 1pt,
table(
stroke: none,
columns: ( 1fr, 1fr, 1fr, 1fr),
..safety_notices,
)
)
#let observation = []
2025-11-09 12:56:49 +01:00
// Materials and Overview
#show table.cell: set text(size: 9pt)
#grid(
columns: (auto, 1fr),
2025-11-09 21:46:12 +01:00
stroke: (bottom:0.5pt + black),
inset: (top: 0pt, bottom: 5pt, left: 5pt, right: 5pt),
2025-11-09 12:56:49 +01:00
grid(
rows: (auto),
text("Material", weight: "bold"),
v(0.4cm),
repeated-table(
num-repeats: tablecolumns,
2025-11-09 21:46:12 +01:00
stroke: (x, y) =>
(if y > 0 { (top: (paint: black, thickness: 0.1pt, dash: "dashed")) } else { none }) +
(if calc.rem(x, 2) == 0 and x > 0 { (left: (paint: black, thickness: 0.1pt, dash: "dashed")) } else { none }),
2025-11-09 12:56:49 +01:00
columns: (auto, auto),
align: (right, left),
..for x in amounts.zip(material_names) {(
..for y in x {(
[#y],
)}
)}
)
),
grid(
text("Übersicht", weight: "bold"),
v(0.4cm),
my-subfig(
..figures,
columns: (auto, auto),
),
v(0.1cm)
),
)
// Durchführung
#grid(
columns: (1fr),
2025-11-09 21:46:12 +01:00
stroke: (bottom:0.5pt + black),
inset: (top: 0pt, bottom: 5pt, left: 5pt, right: 5pt),
2025-11-09 12:56:49 +01:00
grid(
rows: (auto),
text("Durchführung", weight: "bold"),
v(0.4cm),
..steps
),
)
2025-11-09 21:46:12 +01:00
//Interpretation
#grid(
columns: (1fr),
stroke: (bottom:0.5pt + black),
inset: (top: 0pt, bottom: 5pt, left: 5pt, right: 5pt),
grid(
rows: (auto),
text("Interpretation", weight: "bold"),
v(0.4cm),
explanation
),
)
//observation
#grid(
columns: (1fr),
stroke: (bottom:0.5pt + black),
inset: (top: 0pt, bottom: 5pt, left: 5pt, right: 5pt),
grid(
rows: (auto),
text("Observation", weight: "bold"),
v(0.4cm),
observation
),
)
// Safety Notices
2025-11-09 12:56:49 +01:00
// Interpretation