diff --git a/README.md b/README.md index 1594172..d39c2e7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# typst-tablex (v0.0.4) +# typst-tablex (v0.0.5) **More powerful and customizable tables in Typst.** **NOTE:** This library still has a few bugs, but most of them shouldn't be noticeable. **Please open an issue if you find a bug** and I'll get to it as soon as I can. **(Do not be afraid to open issues!! Also, PRs are welcome!)** @@ -17,18 +17,73 @@ * [Basic types and functions](#basic-types-and-functions) * [Gridx and Tablex](#gridx-and-tablex) * [Changelog](#changelog) + * [v0.0.5](#v005) * [v0.0.4](#v004) * [v0.0.3](#v003) * [v0.0.2](#v002) * [v0.0.1](#v001) * [0.1.0 Roadmap](#010-roadmap) +* [License](#license) ## Usage -To use this library, download the file `tablex.typ` from the latest release (or from the repository itself) and place it on the same folder as your own typst file. Then, at the top of your file, write for example `#import "tablex.typ": tablex, cellx` (plus whatever other function you use from the library). +To use this library through the Typst package manager **(for Typst v0.6.0+)**, write for example `#import "@preview/tablex:0.0.5": tablex, cellx` at the top of your Typst file (you may also add whichever other functions you use from the library to that import list!). -This library should be compatible with Typst v0.1.0, v0.2.0, v0.3.0, v0.4.0 and v0.5.0. (Previous versions weren't tested.) -**Using the latest typst version (v0.2.0+) is recommended**, as it fixes certain minor layout issues, and also brings compilation speed improvements. +For older Typst versions, download the file `tablex.typ` from the latest release (or directly from the main branch, for the 'bleeding edge') at the tablex repository (https://github.com/PgBiel/typst-tablex) and place it on the same folder as your own Typst file. Then, at the top of your file, write for example `#import "tablex.typ": tablex, cellx` (plus whichever other functions you use from the library). + +This library should be compatible with Typst v0.2.0, v0.3.0, v0.4.0, v0.5.0 and v0.6.0. +**Using the latest Typst version is recommended (v0.6.0+)**, as it fixes certain bugs which made it almost impossible to use references and citations from within tablex tables (and also brings the package manager, making using tablex even easier!). + +Here's an example of what `tablex` can do: + +![image](https://github.com/PgBiel/typst-tablex/assets/9021226/355c527a-7296-4264-bac7-4ec991b15a18) + +Here's the code for that table: +```js +#import "@preview/tablex:0.0.5": tablex, rowspanx, colspanx + +#tablex( + columns: 4, + align: center + horizon, + auto-vlines: false, + + // indicate the first two rows are the header + // (in case we need to eventually + // enable repeating the header across pages) + header-rows: 2, + + // color the last column's cells + // based on the written number + map-cells: cell => { + if cell.x == 3 and cell.y > 1 { + cell.content = { + let value = int(cell.content.text) + let text-color = if value < 10 { + red.lighten(30%) + } else if value < 15 { + yellow.darken(13%) + } else { + green + } + set text(text-color) + strong(cell.content) + } + } + cell + }, + + /* --- header --- */ + rowspanx(2)[*Username*], colspanx(2)[*Data*], (), rowspanx(2)[*Score*], + (), [*Location*], [*Height*], (), + /* -------------- */ + + [John], [Second St.], [180 cm], [5], + [Wally], [Third Av.], [160 cm], [10], + [Jason], [Some St.], [150 cm], [15], + [Robert], [123 Av.], [190 cm], [20], + [Other], [Unknown St.], [170 cm], [25], +) +``` ## Features @@ -37,17 +92,17 @@ This library should be compatible with Typst v0.1.0, v0.2.0, v0.3.0, v0.4.0 and In most cases, you should be able to replace `#table` with `#tablex` and be good to go for a start - it should look _very_ similar (if not identical). Indeed, the syntax is very similar for the basics: ```js -#import "tablex.typ": tablex +#import "@preview/tablex:0.0.5": tablex #tablex( - columns: (auto, 1em, 1fr, 1fr), // 4 columns - rows: auto, // at least 1 row of auto size - fill: red, - align: center + horizon, - stroke: green, - [a], [b], [c], [d], - [e], [f], [g], [h], - [i], [j], [k], [l] + columns: (auto, 1em, 1fr, 1fr), // 4 columns + rows: auto, // at least 1 row of auto size + fill: red, + align: center + horizon, + stroke: green, + [a], [b], [c], [d], + [e], [f], [g], [h], + [i], [j], [k], [l] ) ``` @@ -62,13 +117,13 @@ This is mostly a word of caution in case anything I haven't anticipated happens, Your cells can now span more than one column and/or row at once, with `colspanx` / `rowspanx`: ```js -#import "tablex.typ": tablex, colspanx, rowspanx +#import "@preview/tablex:0.0.5": tablex, colspanx, rowspanx #tablex( - columns: 3, - colspanx(2)[a], (), [b], - [c], rowspanx(2)[d], [ed], - [f], (), [g] + columns: 3, + colspanx(2)[a], (), [b], + [c], rowspanx(2)[d], [ed], + [f], (), [g] ) ``` @@ -91,30 +146,30 @@ Also, note that, by default, the horizontal lines below the header are transport Example: ```js -#import "tablex.typ": tablex, hlinex, vlinex, colspanx, rowspanx +#import "@preview/tablex:0.0.5": tablex, hlinex, vlinex, colspanx, rowspanx #pagebreak() #v(80%) #tablex( - columns: 4, - align: center + horizon, - auto-vlines: false, - repeat-header: true, - - /* --- header --- */ - rowspanx(2)[*Names*], colspanx(2)[*Properties*], (), rowspanx(2)[*Creators*], - (), [*Type*], [*Size*], (), - /* -------------- */ - - [Machine], [Steel], [5 $"cm"^3$], [John p& Kate], - [Frog], [Animal], [6 $"cm"^3$], [Robert], - [Frog], [Animal], [6 $"cm"^3$], [Robert], - [Frog], [Animal], [6 $"cm"^3$], [Robert], - [Frog], [Animal], [6 $"cm"^3$], [Robert], - [Frog], [Animal], [6 $"cm"^3$], [Robert], - [Frog], [Animal], [6 $"cm"^3$], [Robert], - [Frog], [Animal], [6 $"cm"^3$], [Rodbert], + columns: 4, + align: center + horizon, + auto-vlines: false, + repeat-header: true, + + /* --- header --- */ + rowspanx(2)[*Names*], colspanx(2)[*Properties*], (), rowspanx(2)[*Creators*], + (), [*Type*], [*Size*], (), + /* -------------- */ + + [Machine], [Steel], [5 $"cm"^3$], [John p& Kate], + [Frog], [Animal], [6 $"cm"^3$], [Robert], + [Frog], [Animal], [6 $"cm"^3$], [Robert], + [Frog], [Animal], [6 $"cm"^3$], [Robert], + [Frog], [Animal], [6 $"cm"^3$], [Robert], + [Frog], [Animal], [6 $"cm"^3$], [Robert], + [Frog], [Animal], [6 $"cm"^3$], [Robert], + [Frog], [Animal], [6 $"cm"^3$], [Rodbert], ) ``` @@ -138,40 +193,40 @@ Something similar occurs for `vlinex()`, which has `start`, `end` (first row and Here's some sample usage: ```js -#import "tablex.typ": tablex, gridx, hlinex, vlinex, colspanx, rowspanx +#import "@preview/tablex:0.0.5": tablex, gridx, hlinex, vlinex, colspanx, rowspanx #tablex( - columns: 4, - auto-lines: false, - - // skip a column here vv - vlinex(), vlinex(), vlinex(), (), vlinex(), - colspanx(2)[a], (), [b], [J], - [c], rowspanx(2)[d], [e], [K], - [f], (), [g], [L], - // ^^ '()' after the first cell are 100% ignored + columns: 4, + auto-lines: false, + + // skip a column here vv + vlinex(), vlinex(), vlinex(), (), vlinex(), + colspanx(2)[a], (), [b], [J], + [c], rowspanx(2)[d], [e], [K], + [f], (), [g], [L], + // ^^ '()' after the first cell are 100% ignored ) #tablex( - columns: 4, - auto-vlines: false, - colspanx(2)[a], (), [b], [J], - [c], rowspanx(2)[d], [e], [K], - [f], (), [g], [L], + columns: 4, + auto-vlines: false, + colspanx(2)[a], (), [b], [J], + [c], rowspanx(2)[d], [e], [K], + [f], (), [g], [L], ) #gridx( - columns: 4, - (), (), vlinex(end: 2), - hlinex(stroke: yellow + 2pt), - colspanx(2)[a], (), [b], [J], - hlinex(start: 0, end: 1, stroke: yellow + 2pt), - hlinex(start: 1, end: 2, stroke: green + 2pt), - hlinex(start: 2, end: 3, stroke: red + 2pt), - hlinex(start: 3, end: 4, stroke: blue.lighten(50%) + 2pt), - [c], rowspanx(2)[d], [e], [K], - hlinex(start: 2), - [f], (), [g], [L], + columns: 4, + (), (), vlinex(end: 2), + hlinex(stroke: yellow + 2pt), + colspanx(2)[a], (), [b], [J], + hlinex(start: 0, end: 1, stroke: yellow + 2pt), + hlinex(start: 1, end: 2, stroke: green + 2pt), + hlinex(start: 2, end: 3, stroke: red + 2pt), + hlinex(start: 3, end: 4, stroke: blue.lighten(50%) + 2pt), + [c], rowspanx(2)[d], [e], [K], + hlinex(start: 2), + [f], (), [g], [L], ) ``` @@ -181,16 +236,16 @@ Here's some sample usage: You can also *bulk-customize lines* by specifying `map-hlines: h => new_hline` and `map-vlines: v => new_vline`. This includes any automatically generated lines. For example: -``` -#import "tablex.typ": tablex, colspanx, rowspanx +```js +#import "@preview/tablex:0.0.5": tablex, colspanx, rowspanx #tablex( - columns: 3, - map-hlines: h => (..h, stroke: blue), - map-vlines: v => (..v, stroke: green + 2pt), - colspanx(2)[a], (), [b], - [c], rowspanx(2)[d], [ed], - [f], (), [g] + columns: 3, + map-hlines: h => (..h, stroke: blue), + map-vlines: v => (..v, stroke: green + 2pt), + colspanx(2)[a], (), [b], + [c], rowspanx(2)[d], [ed], + [f], (), [g] ) ``` @@ -214,18 +269,18 @@ Additionally, instead of specifying content to the cell, you can specify a funct For example: ```js -#import "tablex.typ": tablex, cellx, colspanx, rowspanx +#import "@preview/tablex:0.0.5": tablex, cellx, colspanx, rowspanx #tablex( - columns: 3, - fill: red, - align: right, - colspanx(2)[a], (), [beeee], - [c], rowspanx(2)[d], cellx(fill: blue, align: left)[e], - [f], (), [g], - - // place this cell at the first column, seventh row - cellx(colspan: 3, align: center, x: 0, y: 6)[hi I'm down here] + columns: 3, + fill: red, + align: right, + colspanx(2)[a], (), [beeee], + [c], rowspanx(2)[d], cellx(fill: blue, align: left)[e], + [f], (), [g], + + // place this cell at the first column, seventh row + cellx(colspan: 3, align: center, x: 0, y: 6)[hi I'm down here] ) ``` @@ -246,40 +301,40 @@ To customize multiple cells at once, you have a few options: Example: ```js -#import "tablex.typ": tablex, colspanx, rowspanx +#import "@preview/tablex:0.0.5": tablex, colspanx, rowspanx #tablex( - columns: 4, - auto-vlines: true, - - // make all cells italicized - map-cells: cell => { - (..cell, content: emph(cell.content)) - }, - - // add some arbitrary content to entire rows - map-rows: (row, cells) => cells.map(c => - if c == none { - c // keeping 'none' is important - } else { - (..c, content: [#c.content\ *R#row*]) - } - ), - - // color cells based on their columns - // (using 'fill: (column, row) => color' also works - // for this particular purpose) - map-cols: (col, cells) => cells.map(c => - if c == none { - c - } else { - (..c, fill: if col < 2 { blue } else { yellow }) - } - ), + columns: 4, + auto-vlines: true, + + // make all cells italicized + map-cells: cell => { + (..cell, content: emph(cell.content)) + }, + + // add some arbitrary content to entire rows + map-rows: (row, cells) => cells.map(c => + if c == none { + c // keeping 'none' is important + } else { + (..c, content: [#c.content\ *R#row*]) + } + ), + + // color cells based on their columns + // (using 'fill: (column, row) => color' also works + // for this particular purpose) + map-cols: (col, cells) => cells.map(c => + if c == none { + c + } else { + (..c, fill: if col < 2 { blue } else { yellow }) + } + ), - colspanx(2)[a], (), [b], [J], - [c], rowspanx(2)[dd], [e], [K], - [f], (), [g], [L], + colspanx(2)[a], (), [b], [J], + [c], rowspanx(2)[dd], [e], [K], + [f], (), [g], [L], ) ``` @@ -314,7 +369,7 @@ Another example (summing columns): ## Known Issues -- Filled cells will partially overlap with horizontal lines above them. +- Filled cells will partially overlap with horizontal lines above them (see https://github.com/PgBiel/typst-tablex/issues/4). - To be fixed in a future rework of the table drawing process. - Table lines don't play very well with column and row gutter when a colspan or rowspan is used. They may be missing or be cut off by gutters. @@ -323,7 +378,7 @@ Another example (summing columns): - By default, the table assumes that all pages containing it have the same width and height (dimensions). This is used for auto-sizing of columns/rows and for repeatable headers to work properly. It would be potentially costly to re-calculate page sizes on every page, so this was postponed. -- `tablex` can potentially be slower and/or take longer to compile than the default `table` (especially when the table spans a lot of pages). **Please use the latest typst version (v0.2.0+) to reduce this problem** (it brought great optimizations to some internal things). Still, we are looking for ways to better optimize the library (PRs are open!). However, re-compilation is usually fine thanks to typst's built-in memoization. +- `tablex` can potentially be slower and/or take longer to compile than the default `table` (especially when the table spans a lot of pages). **Please use the latest Typst version to reduce this problem** (each version has been bringing further improvements in this sense). Still, we are looking for ways to better optimize the library (see more discussion at https://github.com/PgBiel/typst-tablex/issues/5 - feel free to give some input!). However, re-compilation is usually fine thanks to Typst's built-in memoization. - The internals of the library still aren't very well documented; I plan on adding more info about this eventually. @@ -337,20 +392,20 @@ Another example (summing columns): ```js #let cellx(content, - x: auto, y: auto, - rowspan: 1, colspan: 1, - fill: auto, align: auto, - inset: auto + x: auto, y: auto, + rowspan: 1, colspan: 1, + fill: auto, align: auto, + inset: auto ) = ( - tablex-dict-type: "cell", - content: content, - rowspan: rowspan, - colspan: colspan, - align: align, - fill: fill, - inset: inset, - x: x, - y: y, + tablex-dict-type: "cell", + content: content, + rowspan: rowspan, + colspan: colspan, + align: align, + fill: fill, + inset: inset, + x: x, + y: y, ) ``` where: @@ -369,22 +424,22 @@ Another example (summing columns): ```js #let hlinex( - start: 0, end: auto, y: auto, - stroke: auto, - stop-pre-gutter: auto, gutter-restrict: none, - stroke-expand: true, - expand: none + start: 0, end: auto, y: auto, + stroke: auto, + stop-pre-gutter: auto, gutter-restrict: none, + stroke-expand: true, + expand: none ) = ( - tablex-dict-type: "hline", - start: start, - end: end, - y: y, - stroke: stroke, - stop-pre-gutter: stop-pre-gutter, - gutter-restrict: gutter-restrict, - stroke-expand: stroke-expand, - expand: expand, - parent: none, + tablex-dict-type: "hline", + start: start, + end: end, + y: y, + stroke: stroke, + stop-pre-gutter: stop-pre-gutter, + gutter-restrict: gutter-restrict, + stroke-expand: stroke-expand, + expand: expand, + parent: none, ) ``` @@ -406,22 +461,22 @@ Another example (summing columns): ```js #let vlinex( - start: 0, end: auto, x: auto, - stroke: auto, - stop-pre-gutter: auto, gutter-restrict: none, - stroke-expand: true, - expand: none + start: 0, end: auto, x: auto, + stroke: auto, + stop-pre-gutter: auto, gutter-restrict: none, + stroke-expand: true, + expand: none ) = ( - tablex-dict-type: "vline", - start: start, - end: end, - x: x, - stroke: stroke, - stop-pre-gutter: stop-pre-gutter, - gutter-restrict: gutter-restrict, - stroke-expand: stroke-expand, - expand: expand, - parent: none, + tablex-dict-type: "vline", + start: start, + end: end, + x: x, + stroke: stroke, + stop-pre-gutter: stop-pre-gutter, + gutter-restrict: gutter-restrict, + stroke-expand: stroke-expand, + expand: expand, + parent: none, ) ``` @@ -454,25 +509,25 @@ Another example (summing columns): ```js #let tablex( - columns: auto, rows: auto, - inset: 5pt, - align: auto, - fill: none, - stroke: auto, - column-gutter: auto, row-gutter: auto, - gutter: none, - repeat-header: false, - header-rows: 1, - header-hlines-have-priority: true, - auto-lines: true, - auto-hlines: auto, - auto-vlines: auto, - map-cells: none, - map-hlines: none, - map-vlines: none, - map-rows: none, - map-cols: none, - ..items + columns: auto, rows: auto, + inset: 5pt, + align: auto, + fill: none, + stroke: auto, + column-gutter: auto, row-gutter: auto, + gutter: none, + repeat-header: false, + header-rows: 1, + header-hlines-have-priority: true, + auto-lines: true, + auto-hlines: auto, + auto-vlines: auto, + map-cells: none, + map-hlines: none, + map-vlines: none, + map-rows: none, + map-cols: none, + ..items ) = { // ... } @@ -543,6 +598,19 @@ Another example (summing columns): ## Changelog +### v0.0.5 + +- ⚠️ **Minimum Typst version raised to v0.2.0** +- Improved calculation of page/container dimensions by using the `layout()` function. + - Fixes tables with fractional columns not displaying properly in blocks with `auto` width (https://github.com/PgBiel/typst-tablex/issues/44; https://github.com/PgBiel/typst-tablex/issues/39) + - Fixes some nested tables overflowing the page width (https://github.com/PgBiel/typst-tablex/issues/41) + - Fixes bad interaction between tables with fractional columns and nested tables (https://github.com/PgBiel/typst-tablex/issues/28) + - Fixes table rotation messing up table size calculation (https://github.com/PgBiel/typst-tablex/issues/52) + - Probably fixes other issues not listed here as well. +- Added some guards for infinite lengths and `auto`-sized pages (https://github.com/PgBiel/typst-tablex/issues/47). +- Fixed tablex crashes/improper behavior with `em` strokes and other types of strokes (https://github.com/PgBiel/typst-tablex/issues/49). +- Added the tablex version number as a comment in the source file (as requested in https://github.com/PgBiel/typst-tablex/issues/25). + ### v0.0.4 - Added `typst.toml` to support Typst v0.6.0's soon-to-be-released package manager (see https://github.com/PgBiel/typst-tablex/issues/22). @@ -581,6 +649,7 @@ Initial release. - [ ] General - [X] More docs - [ ] Code cleanup + - [ ] Table drawing rework - [ ] `#table` parity - [X] `columns:`, `rows:` - [X] Basic support @@ -655,3 +724,7 @@ Initial release. - [X] `map-vlines` - Customize each vertical line - [X] `map-rows` - Customize entire rows of cells - [X] `map-cols` - Customize entire columns of cells + +## License + +MIT license (see the `LICENSE` file). diff --git a/tablex-test.pdf b/tablex-test.pdf index 6a1e4a3..3c9e18f 100644 Binary files a/tablex-test.pdf and b/tablex-test.pdf differ diff --git a/tablex-test.typ b/tablex-test.typ index 7d38463..321aa14 100644 --- a/tablex-test.typ +++ b/tablex-test.typ @@ -5,6 +5,7 @@ test deeteeeeereeeedetteeeee +// vvvv causes the dreaded warning (alongside another table downwards) #tablex( columns: (auto, auto, auto), // rows: ((1em, 1em, 1em),) eeee rows: (auto,), @@ -119,6 +120,48 @@ s ) == Examples from the docs +\ +#tablex( + columns: 4, + align: center + horizon, + auto-vlines: false, + + // indicate the first two rows are the header + // (in case we need to eventually + // enable repeating the header across pages) + header-rows: 2, + + // color the last column's cells + // based on the written number + map-cells: cell => { + if cell.x == 3 and cell.y > 1 { + cell.content = { + let value = int(cell.content.text) + let text-color = if value < 10 { + red.lighten(30%) + } else if value < 15 { + yellow.darken(13%) + } else { + green + } + set text(text-color) + strong(cell.content) + } + } + cell + }, + + /* --- header --- */ + rowspanx(2)[*Username*], colspanx(2)[*Data*], (), rowspanx(2)[*Score*], + (), [*Location*], [*Height*], (), + /* -------------- */ + + [John], [Second St.], [180 cm], [5], + [Wally], [Third Av.], [160 cm], [10], + [Jason], [Some St.], [150 cm], [15], + [Robert], [123 Av.], [190 cm], [20], + [Other], [Unknown St.], [170 cm], [25], +) #tablex( columns: (auto, 1em, 1fr, 1fr), // 3 columns @@ -157,7 +200,7 @@ s [f], (), [g], [L], ) -#gridx( +#block(breakable: false, gridx( columns: 4, (), (), vlinex(end: 2), hlinex(stroke: yellow + 2pt), @@ -169,7 +212,7 @@ s [c], rowspanx(2)[d], [e], [K], hlinex(start: 2), [f], (), [g], [L], -) +)) #block(breakable: false, tablex( columns: 3, @@ -270,6 +313,16 @@ s #v(35em) #set page(width: auto, height: auto) +*Auto page tests (infinite dimensions):* + +#table( + columns: 3, + [a], [b], [c], + [d], [e], [f], + [g], [h], [i], + [f], [j], [e\ b\ c\ d], +) + #tablex( columns: 3, [a], [b], [c], @@ -278,6 +331,49 @@ s [f], [j], [e\ b\ c\ d], ) +#table( + columns: (99%, auto), + [a], [b], + [c], [d] +) + +#tablex( + columns: (99%, auto), + [a], [b], + [c], [d] +) + +#table( + columns: (auto, 1fr, 1fr), + [a], [b], [c], + [c], [d], [e] +) + +#tablex( + columns: (auto, 1fr, 1fr), + [a], [b], [c], + [c], [d], [e] +) + +#table( + columns: 4, + gutter: 10pt, + [a], [b], [c], [d], + [a], [b], [c], [d], + [a], [b], [c], [d], + [a], [b], [c], [d], +) + +// vvv causes the dreaded warning (alongside the first table in the file) +#tablex( + columns: 4, + gutter: 10pt, + [a], [b], [c], [d], + [a], [b], [c], [d], + [a], [b], [c], [d], + [a], [b], [c], [d], +) + #set page(width: 300pt, height: 1000pt) #tablex( @@ -425,3 +521,93 @@ Small gutter test: [a], [b], [c], [d], [a], [b], [c], [d], ) + +Test fractional columns in an auto-sized block: + +#block(tablex( + columns: (auto, 1fr, 1fr), + [a], [b], [c], + [d], [e], [f], + [g], [h], [i] +)) + +*Using the examples from issue \#44:* + +1. +#table(columns: 1fr, [1A. table]) +#tablex(columns: 1fr, [1B. tablex]) + +2. +#block(table(columns: 1fr, [2A. table plain block])) +#block(tablex(columns: 1fr, [2B. tablex plain block])) + +3. +#block(breakable: true, table(columns: 1fr, [3A. table breakable: true])) +#block(breakable: true, tablex(columns: 1fr, [3B. tablex breakable: true])) + +4. +#block(breakable: false, table(columns: 1fr, [4A. table breakable: false])) +#block(breakable: false, tablex(columns: 1fr, [4B. tablex breakable: false])) + +*Nested tables from issue \#41:* + +- Triple-nested tables. + +#tablex( + tablex( + tablex( + lorem(10) + ) + ) +) + +- Quadruple-nested tables. + +#tablex( + tablex( + tablex( + tablex( + lorem(20) + ) + ) + ) +) + +*Nested tables from issue \#28:* + +#let mycell = [ + #tablex( + columns: (1fr, 1fr), + [A],[A] + ) +] + += table inside a table +#tablex( + columns: (1fr, 1fr), + mycell, mycell +) + += following table fails +*Problem/Observation*: just one column "C" + +*Expected Outcome*: Two columns + +#tablex( + columns: (1fr, 1fr), + [C],[C] +) + +*Exotic strokes from issue \#49:* + +#tablex( + stroke: 1em, + [C], [C] +) + +// Uncomment after minimum typst version is raised enough for this +// #let s = rect(stroke: (thickness: 1em, miter-limit: 5.0)).stroke +// #tablex( +// stroke: s, +// [C], [C] +// ) diff --git a/tablex.typ b/tablex.typ index 675e4a8..9516a65 100644 --- a/tablex.typ +++ b/tablex.typ @@ -1,5 +1,6 @@ // Welcome to tablex! // Feel free to contribute with any features you think are missing. +// Version: v0.0.5 // -- table counter -- @@ -169,6 +170,11 @@ len } +// Check if this length is infinite. +#let is-infinite-len(len) = { + type(len) in ("ratio", "fraction", "relative length", "length") and "inf" in repr(len) +} + #let validate-cols-rows(columns, rows, items: ()) = { if type(columns) == "integer" { assert(columns >= 0, message: "Error: Cannot have a negative amount of columns.") @@ -200,17 +206,17 @@ } let col_row_is_valid(col_row) = ( - col_row == auto or type(col_row) in ( + (not is-infinite-len(col_row)) and (col_row == auto or type(col_row) in ( "fraction", "length", "relative length", "ratio" - ) + )) ) if not columns.all(col_row_is_valid) { - panic("Invalid column sizes (must all be 'auto' or a valid length specifier).") + panic("Invalid column sizes (must all be 'auto' or a valid, finite length specifier).") } if not rows.all(col_row_is_valid) { - panic("Invalid row sizes (must all be 'auto' or a valid length specifier).") + panic("Invalid row sizes (must all be 'auto' or a valid, finite length specifier).") } let col_len = columns.len() @@ -353,7 +359,9 @@ ) = { page_size = 0pt + page_size - if type(len) == "length" { + if is-infinite-len(len) { + 0pt // avoid the destruction of the universe + } else if type(len) == "length" { if "em" in repr(len) { if styles == none { panic("Cannot convert length to pt ('styles' not specified).") @@ -368,6 +376,10 @@ panic("Cannot convert ratio to pt ('page_size' not specified).") } + if is-infinite-len(page_size) { + return 0pt // page has 'auto' size => % should return 0 + } + ((len / 1%) / 100) * page_size + 0pt // e.g. 100% / 1% = 100; / 100 = 1; 1 * page_size } else if type(len) == "fraction" { if frac_amount == none { @@ -378,7 +390,7 @@ panic("Cannot convert fraction to pt ('frac_total' not specified).") } - if frac_amount <= 0 { + if frac_amount <= 0 or is-infinite-len(frac_total) { return 0pt } @@ -406,7 +418,7 @@ let other_part = len - ratio_part // get the (2em + 5pt) part - let ratio_part_pt = ((ratio_part / 1%) / 100) * page_size + let ratio_part_pt = if is-infinite-len(page_size) { 0pt } else { ((ratio_part / 1%) / 100) * page_size } let other_part_pt = 0pt if other_part < 0pt { @@ -423,10 +435,13 @@ } // Convert a stroke to its thickness -#let stroke-len(stroke, stroke-auto: 1pt) = { +#let stroke-len(stroke, stroke-auto: 1pt, styles: none) = { + let no-ratio-error = "Tablex error: Stroke cannot be a ratio or relative length (i.e. have a percentage like '53%'). Try using the layout() function (or similar) to convert the percentage to 'pt' instead." let stroke = default-if-auto(stroke, stroke-auto) - if type(stroke) in ("length", "relative length") { - stroke + if type(stroke) == "length" { + convert-length-to-pt(stroke, styles: styles) + } else if type(stroke) in ("relative length", "ratio") { + panic(no-ratio-error) } else if type(stroke) == "color" { 1pt } else if type(stroke) == "stroke" { // 2em + blue @@ -434,9 +449,24 @@ let s = repr(stroke).find(r) if s == none { - 1pt + // for more complex strokes, built through dictionaries + // => "thickness: 5pt" field + // note: on typst v0.7.0 or later, can just use 's.thickness' + let r = regex("thickness: (\\d+(?:em|pt|cm|in|%))") + s = repr(stroke).match(r).captures.first() + } + + if s == none { + 1pt // okay it's probably just a color then } else { - eval(s) + let len = eval(s) + if type(len) == "length" { + convert-length-to-pt(len, styles: styles) + } else if type(len) in ("relative length", "ratio") { + panic(no-ratio-error) + } else { + 1pt // should be unreachable + } } } else if type(stroke) == "dictionary" and "thickness" in stroke { stroke.thickness @@ -885,6 +915,10 @@ align(cell_align)[#content] } + if is-infinite-len(inset) { + panic("Tablex error: inset must not be infinite") + } + box( width: width, height: height, inset: inset, fill: cell_fill, @@ -917,7 +951,7 @@ amount-frac += (gutter / 1fr) * (tracks.len() - 1) } - let frac-width = if amount-frac > 0 { + let frac-width = if amount-frac > 0 and not is-infinite-len(remaining) { remaining / amount-frac } else { 0pt @@ -1067,6 +1101,11 @@ } #let fit-auto-columns(available: 0pt, auto_cols: none, columns: none) = { + if is-infinite-len(available) { + // infinite space available => don't modify columns + return columns + } + let remaining = available let auto_cols_remaining = auto_cols.len() @@ -1388,7 +1427,7 @@ height-between(start: y, end: y + rowspan, rows: rows, gutter: gutter, pre-gutter: true) } -// overide start and end for vlines and hlines (keep styling options and stuff) +// override start and end for vlines and hlines (keep styling options and stuff) #let v-or-hline-with-span(v_or_hline, start: none, end: none) = { ( ..v_or_hline, @@ -1505,16 +1544,16 @@ and a.gutter-restrict == b.gutter-restrict ) -#let _largest-stroke-among-lines(lines, stroke-auto: 1pt) = ( - calc.max(0pt, ..lines.map(l => stroke-len(l.stroke, stroke-auto: stroke-auto))) +#let _largest-stroke-among-lines(lines, stroke-auto: 1pt, styles: none) = ( + calc.max(0pt, ..lines.map(l => stroke-len(l.stroke, stroke-auto: stroke-auto, styles: styles))) ) -#let _largest-stroke-among-hlines-at-y(y, hlines: none, stroke-auto: 1pt) = { - _largest-stroke-among-lines(hlines.filter(h => h.y == y), stroke-auto: stroke-auto) +#let _largest-stroke-among-hlines-at-y(y, hlines: none, stroke-auto: 1pt, styles: none) = { + _largest-stroke-among-lines(hlines.filter(h => h.y == y), stroke-auto: stroke-auto, styles: styles) } -#let _largest-stroke-among-vlines-at-x(x, vlines: none, stroke-auto: 1pt) = { - _largest-stroke-among-lines(vlines.filter(v => v.x == x), stroke-auto: stroke-auto) +#let _largest-stroke-among-vlines-at-x(x, vlines: none, stroke-auto: 1pt, styles: none) = { + _largest-stroke-among-lines(vlines.filter(v => v.x == x), stroke-auto: stroke-auto, styles: styles) } // -- end: width/height utilities -- @@ -1563,7 +1602,11 @@ expansion } -#let draw-hline(hline, initial_x: 0, initial_y: 0, columns: (), rows: (), stroke: auto, vlines: (), gutter: none, pre-gutter: false) = { +#let draw-hline( + hline, + initial_x: 0, initial_y: 0, columns: (), rows: (), stroke: auto, vlines: (), gutter: none, pre-gutter: false, + styles: none, +) = { let start = hline.start let end = hline.end let stroke-auto = parse-stroke(stroke) @@ -1581,7 +1624,7 @@ let right-expand = default-if-auto-or-none(expand.at(1), 0pt) if default-if-auto(hline.stroke-expand, true) == true { - let largest-stroke = _largest-stroke-among-vlines-at-x.with(vlines: vlines, stroke-auto: stroke-auto) + let largest-stroke = _largest-stroke-among-vlines-at-x.with(vlines: vlines, stroke-auto: stroke-auto, styles: styles) left-expand += largest-stroke(default-if-auto-or-none(start, 0)) / 2 // expand to the left to close stroke gap right-expand += largest-stroke(default-if-auto-or-none(end, columns.len())) / 2 // close stroke gap to the right } @@ -1612,7 +1655,12 @@ } } -#let draw-vline(vline, initial_x: 0, initial_y: 0, columns: (), rows: (), stroke: auto, gutter: none, hlines: (), pre-gutter: false, stop-before-row-gutter: false) = { +#let draw-vline( + vline, + initial_x: 0, initial_y: 0, columns: (), rows: (), stroke: auto, + gutter: none, hlines: (), pre-gutter: false, stop-before-row-gutter: false, + styles: none +) = { let start = vline.start let end = vline.end let stroke-auto = parse-stroke(stroke) @@ -1630,7 +1678,7 @@ let bottom-expand = default-if-auto-or-none(expand.at(1), 0pt) if default-if-auto(vline.stroke-expand, true) == true { - let largest-stroke = _largest-stroke-among-hlines-at-y.with(hlines: hlines, stroke-auto: stroke-auto) + let largest-stroke = _largest-stroke-among-hlines-at-y.with(hlines: hlines, stroke-auto: stroke-auto, styles: styles) top-expand += largest-stroke(default-if-auto-or-none(start, 0)) / 2 // close stroke gap to the top bottom-expand += largest-stroke(default-if-auto-or-none(end, rows.len())) / 2 // close stroke gap to the bottom } @@ -1735,8 +1783,8 @@ ) = { let width-between = width-between.with(columns: columns, gutter: gutter) let height-between = height-between.with(rows: rows, gutter: gutter) - let draw-hline = draw-hline.with(columns: columns, rows: rows, stroke: stroke, gutter: gutter, vlines: global-vlines) - let draw-vline = draw-vline.with(columns: columns, rows: rows, stroke: stroke, gutter: gutter, hlines: global-hlines) + let draw-hline = draw-hline.with(columns: columns, rows: rows, stroke: stroke, gutter: gutter, vlines: global-vlines, styles: styles) + let draw-vline = draw-vline.with(columns: columns, rows: rows, stroke: stroke, gutter: gutter, hlines: global-hlines, styles: styles) let group-rows = row-group.rows let hlines = row-group.hlines @@ -1813,7 +1861,7 @@ let row_group_height = row_heights + added_header_height + (row_gutter_dy * group-rows.len()) - let is_last_row = pos.y + row_group_height + row_gutter_dy >= max-pos.y + let is_last_row = not is-infinite-len(max-pos.y) and pos.y + row_group_height + row_gutter_dy >= max-pos.y if is_last_row { row_group_height -= row_gutter_dy @@ -2429,15 +2477,15 @@ let map-rows = parse-map-func(map-rows, uses-second-param: true) let map-cols = parse-map-func(map-cols, uses-second-param: true) - locate(t_loc => style(styles => { + layout(size => locate(t_loc => style(styles => { let table_id = _tablex-table-counter.at(t_loc) let page_dimensions = get-page-dim-state(table_id) let page_dim_at = page_dimensions.final(t_loc) let t_pos = t_loc.position() // Subtract the max width/height from current width/height to disregard margin/etc. - let page_width = page_dim_at.width - let page_height = page_dim_at.height + let page_width = size.width + let page_height = size.height let max_pos = default-if-none(page_dim_at.bottom_right, (x: t_pos.x + page_width, y: t_pos.y + page_height)) let min_pos = default-if-none(page_dim_at.top_left, t_pos) @@ -2547,7 +2595,7 @@ ) grid(columns: (auto,), rows: auto, ..row_groups) - })) + }))) } // Same as table but defaults to lines off diff --git a/typst.toml b/typst.toml index a1d880a..e8d1bc9 100644 --- a/typst.toml +++ b/typst.toml @@ -1,6 +1,6 @@ [package] name = "tablex" -version = "0.0.4" +version = "0.0.5" authors = ["PgBiel "] license = "MIT" description = "More powerful and customizable tables in Typst."