I am struggling since several hours when trying to let words within a table cell wrap so that they don’t overlap the table cell’s width. I would appreciate any hint in the right direction.
#set page(
width: 297mm, // A4 landscape width
height: 210mm, // A4 landscape height
margin: 10mm // 10mm margin on all sides
)
#set text(size: 6pt)
#let results = csv("example.csv")
// Filter to match the exact 14 columns
#let expected-headers = (
"id", "volume", "asset_group", "asset_id", "date", "source_type",
"gain_loss", "gain_loss_within_1_year", "gain_loss_ge_1_year",
"consumed_volumes", "years_passed_list", "consumed_ledger_ids",
"gain_loss_list", "source_reference"
)
#let headers = results.at(0) // Extract the first row as headers
#let header-indices = headers.enumerate().filter(((i, h)) => expected-headers.contains(h)).map(((i, _)) => i)
#let filtered-headers = expected-headers // Use exact headers
#let data = results.slice(1) // Remaining rows as data
#let filtered-data = data.map(row => header-indices.map(i => row.at(i)))
// Adjusted table-columns for 14 columns, summing to ~277mm (297mm - 20mm margins)
#let table-columns = (
15mm, // id
20mm, // volume
20mm, // asset_group
20mm, // asset_id
25mm, // date
18mm, // source_type
18mm, // gain_loss
23mm, // gain_loss_within_1_year
23mm, // gain_loss_ge_1_year
20mm, // consumed_volumes
20mm, // years_passed_list
20mm, // consumed_ledger_ids
20mm, // gain_loss_list
15mm // source_reference
) // Total: 277mm
#let wrap-long-content(content) = {
// Debug: Log the type and raw content
let debug-prefix = "TYPE: " + type(content) + " | "
if type(content) == str {
// Debug: Confirm function is processing strings
let result = debug-prefix
// Handle timestamp-like strings
if content.match(regex("\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\+\d{2}")) != none {
let parts = content.split(" ")
if parts.len() == 2 {
result += parts.at(0) + "\n" + parts.at(1)
return result
}
}
// Handle empty or NULL arrays
if content == "{}" or content == "{NULL}" {
return result + ""
}
// Handle arrays (split on commas)
if content.starts-with("{") and content.ends-with("}") {
let trimmed = content.trim("{", "}")
if trimmed.len() == 0 { return result + "" }
let parts = trimmed.split(",")
result += parts.join("\n")
return result
}
// Handle long strings with no spaces
if content.len() > 10 and content.match(regex("\s")) == none {
let chars = content.clusters()
let broken = ""
for (i, c) in chars.enumerate() {
broken += c
if i > 0 and calc.rem(i, 10) == 0 { broken += sym.zws }
}
result += broken
return result
}
return result + content
} else if type(content) == content {
if content.has("text") {
return wrap-long-content(content.text)
}
}
// Fallback for non-string/non-text content
return debug-prefix + repr(content)
}
// General cell styling with wrapping
#show table.cell: it => block(
width: 100%,
breakable: true,
inset: 2pt
)[
#set text(hyphenate: true)
#box(width: 100%, wrap-long-content(it))
]
// Specific header row styling with forced breaking on underscores
#show table.cell.where(y: 0): it => block(
width: 100%,
breakable: true,
inset: 2pt
)[
#set text(hyphenate: true, size: 7pt, weight: "bold")
#let header-text = if type(it.body) == str { it.body } else if it.body.has("text") { it.body.text } else { repr(it.body).trim("[]") }
#box(width: 100%, header-text.replace("_", "_\n"))
]
#table(
columns: table-columns,
table.header(
..filtered-headers.map(h => [#h]), // Convert headers to content
repeat: true // Ensure the header repeats on every page
),
..filtered-data.flatten() // Flatten the filtered data rows
)