这是indexloc提供的服务,不要输入任何密码
Skip to content

Create fit-spans option #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions src/col-row-size.typ
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@
}

// calculate the size of auto columns (based on the max width of their cells)
#let determine-auto-columns(grid: (), styles: none, columns: none, inset: none, align: auto) = {
#let determine-auto-columns(grid: (), styles: none, columns: none, inset: none, align: auto, fit-spans: none) = {
assert(styles != none, message: "Cannot measure auto columns without styles")
let total-auto-size = 0pt
let auto-sizes = ()
Expand All @@ -233,10 +233,20 @@
let pcell = get-parent-cell(cell, grid: grid) // in case this is a colspan
let last-auto-col = get-colspan-last-auto-col(pcell, columns: columns)

let fit-this-span = if "fit-spans" in pcell and pcell.fit-spans != auto {
pcell.fit-spans.x
} else {
fit-spans.x
}
let this-cell-can-expand-columns = pcell.colspan == 1 or not fit-this-span

// only expand the last auto column of a colspan,
// and only the amount necessary that isn't already
// covered by fixed size columns.
if last-auto-col == i {
// However, ignore this cell if it is a colspan with
// `fit-spans.x == true` (it requests to not expand
// columns).
if last-auto-col == i and this-cell-can-expand-columns {
// take extra inset as extra width or height on 'auto'
let cell-inset = default-if-auto(pcell.inset, inset)

Expand Down Expand Up @@ -347,7 +357,7 @@
columns
}

#let determine-column-sizes(grid: (), page-width: 0pt, styles: none, columns: none, inset: none, align: auto, col-gutter: none) = {
#let determine-column-sizes(grid: (), page-width: 0pt, styles: none, columns: none, inset: none, align: auto, col-gutter: none, fit-spans: none) = {
let columns = columns.map(c => {
if type(c) in (_length-type, _rel-len-type, _ratio-type) {
convert-length-to-pt(c, styles: styles, page-size: page-width)
Expand All @@ -373,7 +383,7 @@
// page-width == 0pt => page width is 'auto'
// so we don't have to restrict our table's size
if available-size >= 0pt or page-width == 0pt {
let auto-cols-result = determine-auto-columns(grid: grid, styles: styles, columns: columns, inset: inset, align: align)
let auto-cols-result = determine-auto-columns(grid: grid, styles: styles, columns: columns, inset: inset, align: align, fit-spans: fit-spans)
let total-auto-size = auto-cols-result.total
let auto-sizes = auto-cols-result.sizes
columns = auto-cols-result.columns
Expand Down Expand Up @@ -427,7 +437,7 @@
}

// calculate the size of auto rows (based on the max height of their cells)
#let determine-auto-rows(grid: (), styles: none, columns: none, rows: none, align: auto, inset: none) = {
#let determine-auto-rows(grid: (), styles: none, columns: none, rows: none, align: auto, inset: none, fit-spans: none) = {
assert(styles != none, message: "Cannot measure auto rows without styles")
let total-auto-size = 0pt
let auto-sizes = ()
Expand All @@ -445,10 +455,20 @@
let pcell = get-parent-cell(cell, grid: grid) // in case this is a rowspan
let last-auto-row = get-rowspan-last-auto-row(pcell, rows: rows)

let fit-this-span = if "fit-spans" in pcell and pcell.fit-spans != auto {
pcell.fit-spans.y
} else {
fit-spans.y
}
let this-cell-can-expand-rows = pcell.rowspan == 1 or not fit-this-span

// only expand the last auto row of a rowspan,
// and only the amount necessary that isn't already
// covered by fixed size rows.
if last-auto-row == i {
// However, ignore this cell if it is a rowspan with
// `fit-spans.y == true` (it requests to not expand
// rows).
if last-auto-row == i and this-cell-can-expand-rows {
let width = get-colspan-fixed-size-covered(pcell, columns: columns)

// take extra inset as extra width or height on 'auto'
Expand Down Expand Up @@ -487,7 +507,7 @@
(total: total-auto-size, sizes: auto-sizes, rows: new-rows)
}

#let determine-row-sizes(grid: (), page-height: 0pt, styles: none, columns: none, rows: none, align: auto, inset: none, row-gutter: none) = {
#let determine-row-sizes(grid: (), page-height: 0pt, styles: none, columns: none, rows: none, align: auto, inset: none, row-gutter: none, fit-spans: none) = {
let rows = rows.map(r => {
if type(r) in (_length-type, _rel-len-type, _ratio-type) {
convert-length-to-pt(r, styles: styles, page-size: page-height)
Expand All @@ -497,7 +517,7 @@
})

let auto-rows-res = determine-auto-rows(
grid: grid, columns: columns, rows: rows, styles: styles, align: align, inset: inset
grid: grid, columns: columns, rows: rows, styles: styles, align: align, inset: inset, fit-spans: fit-spans
)

let auto-size = auto-rows-res.total
Expand Down Expand Up @@ -545,13 +565,15 @@
columns: none, rows: none,
inset: none, gutter: none,
align: auto,
fit-spans: none,
) = {
let columns-res = determine-column-sizes(
grid: grid,
page-width: page-width, styles: styles, columns: columns,
inset: inset,
align: align,
col-gutter: gutter.col
col-gutter: gutter.col,
fit-spans: fit-spans
)
columns = columns-res.columns
gutter.col = columns-res.gutter
Expand All @@ -563,7 +585,8 @@
rows: rows,
inset: inset,
align: align,
row-gutter: gutter.row
row-gutter: gutter.row,
fit-spans: fit-spans
)
rows = rows-res.rows
gutter.row = rows-res.gutter
Expand Down
29 changes: 29 additions & 0 deletions src/common.typ
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,32 @@
#let array-sum(arr, zero: 0) = {
arr.fold(zero, (a, x) => a + x)
}

// -- common validators --

// Converts the 'fit-spans' argument to a (x: bool, y: bool) dictionary.
// Optionally use a default dictionary to fill missing arguments with.
// This is in common.typ as it is needed by grid.typ as well.
#let validate-fit-spans(fit-spans, default: (x: false, y: false), error-prefix: none) = {
if type(error-prefix) == _str-type {
error-prefix = " " + error-prefix
} else {
error-prefix = ""
}
if type(fit-spans) == _bool-type {
fit-spans = (x: fit-spans, y: fit-spans)
}
if type(fit-spans) == _dict-type {
assert(fit-spans.len() > 0, message: "Tablex error:" + error-prefix + " 'fit-spans', if a dictionary, must not be empty.")
assert(fit-spans.keys().all(k => k in ("x", "y")), message: "Tablex error:" + error-prefix + " 'fit-spans', if a dictionary, must only have the keys x and y.")
assert(fit-spans.values().all(v => type(v) == _bool-type), message: "Tablex error:" + error-prefix + " keys 'x' and 'y' in the 'fit-spans' dictionary must be booleans (true/false).")
for key in ("x", "y") {
if key in default and key not in fit-spans {
fit-spans.insert(key, default.at(key))
}
}
} else {
panic("Tablex error:" + error-prefix + " Expected 'fit-spans' to be either a boolean or dictionary, found '" + str(type(fit-spans)) + "'")
}
fit-spans
}
9 changes: 8 additions & 1 deletion src/grid.typ
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@

// Organize cells in a grid from the given items,
// and also get all given lines
#let generate-grid(items, x-limit: 0, y-limit: 0, map-cells: none) = {
#let generate-grid(items, x-limit: 0, y-limit: 0, map-cells: none, fit-spans: none) = {
// init grid as a matrix
// y-limit x x-limit
let grid = create-grid(x-limit, y-limit)
Expand Down Expand Up @@ -286,6 +286,13 @@

cell.content = content

// resolve 'fit-spans' option for this cell
if "fit-spans" not in cell {
cell.fit-spans = auto
} else if cell.fit-spans != auto {
cell.fit-spans = validate-fit-spans(cell.fit-spans, default: fit-spans, error-prefix: "At cell (" + str(this-x) + ", " + str(this-y) + "):")
}

// up to which 'y' does this cell go
let max-x = this-x + cell.colspan - 1
let max-y = this-y + cell.rowspan - 1
Expand Down
2 changes: 2 additions & 0 deletions src/option-parsing.typ
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@
header-hlines-have-priority
}

// 'validate-fit-spans' is needed by grid, and is thus in common.typ

#let validate-renderer(renderer) = {
assert(renderer in ("old", "cetz"), message: "Tablex error: 'renderer' option must be either \"old\" or \"cetz\".")

Expand Down
32 changes: 30 additions & 2 deletions src/tablex.typ
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,30 @@
// 'none' if they're a position taken by a cell in a
// colspan/rowspan.
//
// fit-spans: Determine if rowspans and colspans should fit within their
// spanned 'auto'-sized tracks (columns and rows) instead of causing them to
// expand based on the rowspan/colspan cell's size. (Most users of tablex
// shouldn't have to change this option.)
// Must either be a dictionary '(x: true/false, y: true/false)' or a boolean
// true/false (which is converted to the (x: value, y: value) format with both
// 'x' and 'y' being set to the same value; for instance, 'true' becomes
// '(x: true, y: true)').
// Setting 'x' to 'false' (the default) means that colspans will cause the last
// (rightmost) auto column they span to expand if the cell's contents are too
// long; setting 'x' to 'true' negates this, and auto columns will ignore the
// size of colspans. Similarly, setting 'y' to 'false' (the default) means that
// rowspans will cause the last (bottommost) auto row they span to expand if
// the cell's contents are too tall; setting 'y' to 'true' causes auto rows to
// ignore the size of rowspans.
// This setting is mostly useful when you have a colspan or a rowspan spanning
// tracks with fractional (1fr, 2fr, ...) size, which can cause the fractional
// track to have less or even zero size, compromising all other cells in it.
// If you're facing this problem, you may want experiment with setting this
// option to '(x: true)' (if this is affecting columns) or 'true' (for rows
// too, same as '(x: true, y: true)').
// Note that this option can also be set in a per-cell basis through cellx().
// See its reference for more information.
//
// renderer: Choose the renderer you will use.
// Must be either "old" or "cetz".
// Defaults to "old".
Expand Down Expand Up @@ -126,6 +150,7 @@
map-vlines: none,
map-rows: none,
map-cols: none,
fit-spans: false,
renderer: "old",
renderer-args: (:),
..items
Expand All @@ -138,6 +163,7 @@
let map-vlines = validate-map-func(map-vlines)
let map-rows = validate-map-func(map-rows)
let map-cols = validate-map-func(map-cols)
let fit-spans = validate-fit-spans(fit-spans, default: (x: false, y: false))
let renderer = validate-renderer(renderer)
let renderer-args = validate-renderer-args(renderer-args, renderer: renderer)

Expand All @@ -157,7 +183,8 @@
let grid-info = generate-grid(
items,
x-limit: col-len, y-limit: row-len,
map-cells: map-cells
map-cells: map-cells,
fit-spans: fit-spans
)

let table-grid = grid-info.grid
Expand Down Expand Up @@ -232,7 +259,8 @@
styles: styles,
columns: columns, rows: rows,
inset: inset, align: align,
gutter: gutter
gutter: gutter,
fit-spans: fit-spans
)

let columns = updated-cols-rows.columns
Expand Down
2 changes: 2 additions & 0 deletions src/types.typ
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
rowspan: 1, colspan: 1,
fill: auto, align: auto,
inset: auto,
fit-spans: auto,
render: none,
) = (
tablex-dict-type: "cell",
Expand All @@ -58,6 +59,7 @@
align: align,
fill: fill,
inset: inset,
fit-spans: fit-spans,
render: render,
x: x,
y: y,
Expand Down
98 changes: 98 additions & 0 deletions tablex-test.typ
Original file line number Diff line number Diff line change
Expand Up @@ -1166,3 +1166,101 @@ Combining em and pt (with a stroke object):
#tablex(columns: (auto, auto, auto, auto),
[lorem_ipsum_dolor_sit_amet], [lorem], [lorem_ipsum_dolor_sit_amet_consectetur_adipisici], [lorem],
)

*Rowspans with 1fr and auto using 'fit-spans' (Issues \#56 and \#78)*

#[
#let unbreakable-tablex(..args) = block(breakable: false, tablex(..args))

_For issue \#78_

- Table is normal:

#unbreakable-tablex(
columns: (1fr, 1fr, auto, auto, auto),
[a], [b], [c], [d], [e],
cellx(colspan: 5)[#lorem(5)],
[a], [b], [c], [d], [e],
cellx(colspan: 2)[#lorem(10)], none, none, none,
[a], [b], [c], [d], [e],
)

- Table has overlap:

#unbreakable-tablex(
columns: (1fr, 1fr, auto, auto, auto),
[a], [b], [c], [d], [e],
cellx(colspan: 5)[#lorem(5)],
[a], [b], [c], [d], [e],
cellx(colspan: 2)[#lorem(10)], none, none, none,
[a], [b], [c], [d], [e],
cellx(colspan: 3)[#lorem(15)], none, none,
)

- Table no longer has overlap:

#unbreakable-tablex(
columns: (1fr, 1fr, auto, auto, auto),
fit-spans: (x: true),
[a], [b], [c], [d], [e],
cellx(colspan: 5)[#lorem(5)],
[a], [b], [c], [d], [e],
cellx(colspan: 2)[#lorem(10)], none, none, none,
[a], [b], [c], [d], [e],
cellx(colspan: 3)[#lorem(15)], none, none,
)

_For issue \#56_

- Table is normal:

#unbreakable-tablex(
columns: (auto, auto, 1fr),
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D]
)

- Second column too large:

#unbreakable-tablex(
columns: (auto, auto, 1fr),
colspanx(3)[Hello world! Hello!],
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D]
)

- Second column is now normal:

#unbreakable-tablex(
columns: (auto, auto, 1fr),
fit-spans: (x: true),
colspanx(3)[Hello world! Hello!],
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D]
)

#unbreakable-tablex(
columns: (auto, auto, 1fr),
fit-spans: true,
colspanx(3)[Hello world! Hello!],
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D]
)

#unbreakable-tablex(
columns: (auto, auto, 1fr),
colspanx(3, fit-spans: (x: true))[Hello world! Hello!],
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D],
[A], [BC], [D]
)
]