#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 chemscript( lan, title, title_fontsize, ghs_signs, tablebreak_bias, step_descriptions, step_image_paths, step_image_size, image_paths, image_radius, amounts, material_names, safety_notices, explanation, observation ) = { let tablecolumns = calc.ceil(amounts.len() / tablebreak_bias) let materials = material_names.map(name => text(name)) let has_images = step_image_paths.any(path => path != "" and path != "none") set text(lang:lan) 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 // NOTE: For Linebreaks, use \n in the text 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: (top: 5pt, bottom: 5pt, left: 5pt, right: 0pt), 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: image_radius, image(img) )) } else { none }, ) }) let figures = image_paths.map(path => block( clip: true, radius: image_radius, image(path) )) //title and hazard grid( columns: (1fr, auto), align: (horizon, right+horizon), stroke: (bottom:0.5pt + black), inset: (5pt), par( text(title, size: title_fontsize)), grid( columns: ghs_signs.len(), rows: 30pt, ..ghs_signs.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, ) ) // 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