#import "@preview/typsium-ghs:0.1.0": * #import "@preview/one-liner:0.2.0": fit-to-width #import "@preview/subpar:0.2.2" #import "@preview/typsium:0.3.0":* #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) #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", "none", "figures/dummy.jpg", ) #let safety_notices = (h-statement(310), h-statement(310)) #let step_descriptions = ( "Essen sie alle Kinder", "Essen sie alle Fische", lorem(10), ) // 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 #let tablecolumns = calc.ceil(amounts.len() / 7) #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( 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)), if img != "none" { pad(right: 10pt, block( clip: true, radius: imageradius, image(img) )) } else { none }, ) }) #let figures = image_paths.map(path => block( clip: true, radius: imageradius, image(path) )) //title and hazard #grid( columns: (0.7fr, 1fr), align: (horizon, right+horizon), stroke: (bottom:0.5pt + black), inset: (5pt), par( text("Methan-Mamba", size: 22pt)), grid( columns: 9, rows: 30pt, ..range(1,10).map(x=> ghs(x)) ) ) //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 = [] // Materials and Overview #show table.cell: set text(size: 9pt) #grid( columns: (auto, 1fr), stroke: (bottom:0.5pt + black), inset: (top: 0pt, bottom: 5pt, left: 5pt, right: 5pt), grid( rows: (auto), text("Material", weight: "bold"), v(0.4cm), repeated-table( num-repeats: tablecolumns, 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 }), 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), stroke: (bottom:0.5pt + black), inset: (top: 0pt, bottom: 5pt, left: 5pt, right: 5pt), grid( rows: (auto), text("Durchführung", weight: "bold"), v(0.4cm), ..steps ), ) //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 // Interpretation