Offer-motivated features: :hline defaults, ranged :hpos, @date :days, :bottom none

- @table: a writer's :hline/:vline replaces the default lines; new
  boundary name 'none' removes all lines
- @table: ranged :hpos argument overrides :cell_hpos per cell
  (\multicolumn{1} in tex; positions a colspan anchor's merged cell)
- @date/@datetime: :days offset argument (sks/date/date.py)
- @document: ":bottom none" suppresses the footer (\pagestyle{empty})

Also carries the escape-system generator/renderer fixes, per-cell-range
:format, and uppercase .TTF/.OTF font recognition from klammertext-dev.
This commit is contained in:
2026-07-24 21:37:58 +02:00
parent 8a2699a253
commit 4262fc6136
14 changed files with 657 additions and 146 deletions

View File

@@ -3,9 +3,10 @@
@@@argtype index_subsets |
one or more subsets in parentheses, attached to an index. Each subset is a
single index <n>, a closed range <n>-<m>, or an open range <n>- (from <n> to
the end). Several subsets are separated by commas, with no spaces.
the end). A negative index counts from the end (-1 is the last). Several
subsets are separated by commas, with no spaces.
Example: (1-4,6-9)
:pattern \((?^:\d+(?^:-\d*)?)(?^:,\d+(?^:-\d*)?)*\)
:pattern \((?^:-?\d+(?^:-(?^:-?\d+)?)?)(?^:,-?\d+(?^:-(?^:-?\d+)?)?)*\)
@@@
@@@argtype indexed_range |
@@ -13,14 +14,18 @@
single index <i>, a closed index range <i>-<j>, or an open index range <i>-
(from <i> to the last index). It may be followed by parenthesized subsets
(see the index_subsets type) restricting the extent in the other dimension.
All indices are zero-origin. Examples:
All indices are zero-origin. A negative index counts from the end, as in
Python: -1 is the last index, -2 the second to last. Ranges are inclusive,
so "1--2" is index 1 through the second-to-last index. Examples:
3 index 3, full extent
2-5 indices 2 through 5, full extent
3(1-4,6-9) index 3, restricted to 1 through 4 and 6 through 9
2-5(0-2) indices 2 through 5, each restricted to 0 through 2
-1 the last index
1--2 index 1 through the second-to-last index
:pattern \d+(?^:-\d*)?(?^:'index_subsets')?
:pattern -?\d+(?^:-(?^:-?\d+)?)?(?^:'index_subsets')?
@@@
@@@argtype column_width |
@@ -51,16 +56,19 @@
is either a boundary name or an indexed_range of boundary indices. The
names are 'top' (boundary 0), 'head' (boundary 1, under a header row),
'bottom' (boundary N), 'inner' (all boundaries between top and bottom),
and 'all' (every boundary). A name or index may be followed by
parenthesized subsets to draw only part of a line, given as zero-origin
column ranges. Examples:
'all' (every boundary), and 'none' (no lines). A name or index may be
followed by parenthesized subsets to draw only part of a line, given as
zero-origin column ranges. When ^:hline is given it replaces the default
lines (with a header, 'head' and 'bottom'); ^:hline none removes them.
Examples:
top bottom lines above and below the table
head(1-) a line under the header, from column 1 to the last
3(1-4,6-9) two partial lines at boundary 3
all every line
none no lines
:pattern ((?^:top^|head^|inner^|bottom^|all)(?^:'index_subsets')?^|'indexed_range'^|\s+)+
:pattern ((?^:top^|head^|inner^|bottom^|all^|none)(?^:'index_subsets')?^|'indexed_range'^|\s+)+
:python_cast (lambda s : s.split())
@@@
@@ -71,15 +79,15 @@
the left; boundary i lies to the left of column i, and boundary M is the
right edge. An item is either a boundary name or an indexed_range of
boundary indices. The names are 'outer' (boundaries 0 and M), 'inner'
(all boundaries between them), and 'all' (every boundary). A name or
index may be followed by parenthesized subsets to draw only part of a
line, given as zero-origin row ranges. Examples:
(all boundaries between them), 'all' (every boundary), and 'none' (no
lines). A name or index may be followed by parenthesized subsets to
draw only part of a line, given as zero-origin row ranges. Examples:
outer lines at the left and right edges
2(0-3) a line left of column 2, spanning rows 0 through 3
all every line
:pattern ((?^:outer^|inner^|all)(?^:'index_subsets')?^|'indexed_range'^|\s+)+
:pattern ((?^:outer^|inner^|all^|none)(?^:'index_subsets')?^|'indexed_range'^|\s+)+
:python_cast (lambda s : s.split())
@@@
@@ -105,22 +113,35 @@
<target> = <operator> <operand> <operand> ...
where the target is a single cell written <row>(<column>) with zero-origin
indices, the operator is one of + - * /, and each operand is either a cell
selection or a number. A cell selection is an indexed_range read as
<rows>(<columns>); a range expands to all of its cells in row order, so
"+ 1-2(3)" sums column 3 of rows 1 and 2. A plain number is a constant
where the operator is one of + - * / and each operand is a cell selection
or a number. A cell selection is an indexed_range read as <rows>(<columns>);
a range expands to all of its cells in row order, so "+ 1-2(3)" sums column 3
of rows 1 and 2. The SHAPE of the target chooses the operation: a single
cell <row>(<column>) folds the operands to one value, while a target that
ranges over rows (0-(2)) or columns (-1(0-)) runs the calculation once per
row or column (a "map"). See notes/calc_notation.md for the map forms,
relative operands, and broadcasting. A plain number is a constant
and always uses a period as its decimal mark. Operators fold from the
left ("- 1(0-2)" is a minus b minus c); with a single operand, - negates
and / gives the reciprocal. Calculations run in the order given, and each
reads the values earlier calculations have written, as displayed.
Example:
Negative indices count from the end (see indexed_range), which is handy
when a total sits in the last row: "-1(5) = + 1--2(5)" writes into the
last row of column 5 the sum of that column from row 1 through the row
above it. Example:
1(3) = * 1(1-2) ;
2(3) = * 2(1-2) ;
3(3) = + 1-2(3)
:pattern \s*(\d+\(\d+\)\s*=\s*[-+*/](\s+(\d+(?^:-\d*)?'index_subsets'^|'float'))+\s*(;\s*^|\s*$))+
# Coarse structural check only -- "<target> = <op> <operand>..." groups
# separated by ";" -- so that a malformed target or operand reaches
# run_calc() in table.py, whose per-token validation gives a precise message
# (e.g. an infix "* a * b" reports that "*" is not a number or a cell
# selection) instead of this whole description being dumped. A token is any
# run of characters other than space, ";", or "=" ("^^" escapes the regex
# class negation "[^...]" so the Klammertext "^" is not consumed).
:pattern \s*([^^\s;=]+\s*=\s*[-+*/](\s+[^^\s;=]+)+\s*(;\s*^|\s*$))+
@@@
@@ -133,10 +154,33 @@
:default period
@@@
@@@argtype format_spec |
a Python format specification applied to calculated cell values, for
example ",.2f" for two decimal places with grouped thousands.
:pattern \S+
@@@argtype table_hpos |
cell position overrides, as one or more <cells> <position> pairs
separated by semicolons (the same list style as ^:calc). <cells> is an
indexed_range selecting cells; <position> is l, c, or r and overrides
the column position given by ^:cell_hpos for those cells. A colspan
anchor's override positions the whole merged cell. For example,
"-3--1(3) r" right-justifies the cells in column 3 of the last three
rows.
# Coarse check ("<cells> <position>" pairs); hpos_overrides() in
# table.py validates the range and position.
:pattern \s*([^^\s;]+\s+[lcr]\s*(;\s*^|\s*$))+
@@@
@@@argtype table_format |
cell formatting, as one or more <cells> <function> pairs separated by
semicolons (the same list style as :calc). <cells> is an indexed_range
selecting the cells to format; <function> is a <module>.<function> Python
reference (the same form the eval klammer uses) to a function that takes the
cell's value and the target and returns the formatted text. The function
lives in a module the writer supplies (a currency style is specific to a
document, so the SKS ships none): for example, with a euro() function in
your own module, "0-(5) myformats.euro" formats every cell of column 5 as a
Euro amount. Runs after :calc, so it formats computed values; a cell whose
value is not a number is left unchanged, with a warning.
# Coarse check ("<cells> <function>" pairs); apply_formats() in table.py
# validates the range and calls the function.
:pattern \s*([^^\s;]+\s+[^^\s;]+\s*(;\s*^|\s*$))+
@@@
@@rowcolor.tex s : \colorrow{*s*} @@
@@ -153,12 +197,13 @@
:vline.table_vline
:grid.bool false
:cell_hpos.cell_hpos
:hpos.table_hpos
:header_font.font i
:font.font_list
:colspan.table_span
:rowspan.table_span
:calc.table_calc
:calc_format.format_spec
:format.table_format
:decimal.decimal_mark
:leading.float 1.3
:colsep 4pt