2026-07-18 18:48:23 +02:00
|
|
|
import functools
|
|
|
|
|
import collections
|
2026-07-24 21:37:58 +02:00
|
|
|
import importlib
|
2026-07-18 18:48:23 +02:00
|
|
|
import re
|
|
|
|
|
import sys
|
|
|
|
|
import traceback
|
|
|
|
|
import pprint
|
|
|
|
|
|
|
|
|
|
import kutil
|
|
|
|
|
import klammer_base
|
|
|
|
|
import html_util
|
|
|
|
|
from html_util import E
|
|
|
|
|
import latex_util
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
from indexed_range import Indexed_ranges, hline_names, vline_names
|
2026-07-18 18:48:23 +02:00
|
|
|
import table_cell
|
|
|
|
|
import font
|
|
|
|
|
|
2026-07-24 21:37:58 +02:00
|
|
|
# ---- :format functions ---------------------------------------------------
|
|
|
|
|
# A :format function takes (value, target) and returns the formatted cell
|
|
|
|
|
# text. It is named <module>.<function> in the :format list (like an eval
|
|
|
|
|
# reference), so it can live in ANY module: the SKS ships none by default, and
|
|
|
|
|
# a document defines its own (e.g. a euro() in the document's .py, named
|
|
|
|
|
# "<module>.euro" in :format) because a specific currency style is a property
|
|
|
|
|
# of that document, not of the SKS. A function should emit period-decimal
|
|
|
|
|
# numbers; apply_formats() applies the :decimal comma swap.
|
|
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
def extend(lst, count, fill=None):
|
|
|
|
|
if isinstance(lst, str):
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
lst = lst.split()
|
2026-07-18 18:48:23 +02:00
|
|
|
if fill is None:
|
|
|
|
|
fill = lst[-1] if lst else ""
|
|
|
|
|
return lst + ([fill] * (count - len(lst)))
|
|
|
|
|
|
|
|
|
|
def parse_hpos(target, pos):
|
|
|
|
|
if '"' in pos:
|
|
|
|
|
pos_list = []
|
|
|
|
|
parts = pos.split('"')
|
|
|
|
|
i = 0
|
|
|
|
|
while i < len(parts):
|
|
|
|
|
pos_list += parts[i].split()
|
|
|
|
|
i += 1
|
|
|
|
|
if i == len(parts):
|
|
|
|
|
break
|
|
|
|
|
#pos_list.append(f"{{{parts[i]}}}")
|
|
|
|
|
pos_list.append(kutil.parse_length(target, f'"{parts[i]}"')[0])
|
|
|
|
|
i += 1
|
|
|
|
|
else:
|
|
|
|
|
# pos_list = pos.split()
|
|
|
|
|
pos_list = pos
|
|
|
|
|
#print("pos_list:", pos_list)
|
|
|
|
|
return pos_list
|
|
|
|
|
|
|
|
|
|
class Table(klammer_base.Klammer_base):
|
|
|
|
|
id = 0
|
|
|
|
|
def __init__(self, K):
|
|
|
|
|
super().__init__(K)
|
2026-07-24 21:37:58 +02:00
|
|
|
self.row_count = len(self.rows)
|
2026-07-18 18:48:23 +02:00
|
|
|
if self.grid:
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
self.vline = ["all"]
|
|
|
|
|
self.hline = ["all"]
|
2026-07-24 21:37:58 +02:00
|
|
|
elif not self.hline and self.header:
|
|
|
|
|
# Default lines: under the header row and at the bottom. A
|
|
|
|
|
# writer's own :hline replaces them (":hline none" = no lines).
|
|
|
|
|
self.hline = ["1", str(self.row_count)]
|
2026-07-18 18:48:23 +02:00
|
|
|
self.row_size = max([len(e) for e in self.rows])
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
# Rows with fewer cells than the widest row are padded with empty
|
|
|
|
|
# cells (last-value duplication is for argument lists, not content).
|
|
|
|
|
self.rows = [row + [""] * (self.row_size - len(row)) for row in self.rows]
|
|
|
|
|
self.s_vline = Indexed_ranges(self.row_size + 1, self.row_count - 1, self.vline,
|
|
|
|
|
vline_names(self.row_size + 1), ":vline")
|
|
|
|
|
self.s_hline = Indexed_ranges(self.row_count + 1, self.row_size - 1, self.hline,
|
|
|
|
|
hline_names(self.row_count + 1), ":hline")
|
|
|
|
|
self.s_rowspan = Indexed_ranges(self.row_size, self.row_count - 1, self.rowspan,
|
|
|
|
|
argument=":rowspan")
|
|
|
|
|
self.s_colspan = Indexed_ranges(self.row_count, self.row_size - 1, self.colspan,
|
|
|
|
|
argument=":colspan")
|
2026-07-24 21:37:58 +02:00
|
|
|
# :calc runs after the span structures exist so calculate() can warn
|
|
|
|
|
# when a target lands in a cell hidden by a colspan/rowspan merge;
|
|
|
|
|
# :format runs after :calc so it formats the computed values.
|
|
|
|
|
if self.calc:
|
|
|
|
|
self.compute_coverage()
|
|
|
|
|
self.calculate()
|
|
|
|
|
if self.format:
|
|
|
|
|
self.apply_formats()
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
self.cell_hpos = extend(parse_hpos(self.K_target, self.cell_hpos), self.row_size)
|
2026-07-18 18:48:23 +02:00
|
|
|
#self.cell_hpos = self.cell_hpos.split(";")
|
|
|
|
|
self.font = extend(self.font, self.row_size)
|
2026-07-25 21:16:21 +02:00
|
|
|
# In html, a 'fit' column mixed with sized columns must be clamped
|
|
|
|
|
# to its widest entry (tex's \widthof semantics) or it absorbs the
|
|
|
|
|
# window width: its cells get nowrap (class Wfit; see table.css).
|
|
|
|
|
# In a full-width table (fractions/'*') they also need the classic
|
|
|
|
|
# 1%-shrink width (class Wpct) to survive surplus distribution; in
|
|
|
|
|
# a 'fill' table the table is content-sized (max-width) and a
|
|
|
|
|
# percentage would blow it up to full width, so nowrap alone.
|
|
|
|
|
# All-'fit' tables shrink to content anyway and keep wrapping.
|
|
|
|
|
widths = extend(self.column_width, self.row_size)
|
|
|
|
|
self.fill_columns = [i for i, w in enumerate(widths) if w == "fill"]
|
|
|
|
|
if self.fill_columns:
|
|
|
|
|
if any(w not in ("fit", "f", "fill") for w in widths):
|
|
|
|
|
raise Exception(
|
|
|
|
|
':column_width: "fill" cannot be combined with a '
|
|
|
|
|
'fraction or "*" -- fill computes the remaining width '
|
|
|
|
|
'itself')
|
|
|
|
|
if len(self.fill_columns) > 4:
|
|
|
|
|
raise Exception(
|
|
|
|
|
':column_width: at most four "fill" columns are '
|
|
|
|
|
'supported')
|
|
|
|
|
mixed = not all(w in ("fit", "f") for w in widths)
|
|
|
|
|
self.fit_columns = {i for i, w in enumerate(widths)
|
|
|
|
|
if mixed and w in ("fit", "f")}
|
|
|
|
|
self.fit_class = "Wfit" if self.fill_columns else "Wfit Wpct"
|
|
|
|
|
# A table edge with no outer vertical line drops its outer cell
|
|
|
|
|
# padding (html Fl/Fr classes, tex @{}) so the edge cells' text
|
|
|
|
|
# aligns with the text margin; with an outer line the padding
|
|
|
|
|
# stays -- text against a border looks worse than text inset
|
|
|
|
|
# from a margin.
|
|
|
|
|
self.flush_l = 0 not in self.s_vline.by_index
|
|
|
|
|
self.flush_r = self.row_size not in self.s_vline.by_index
|
|
|
|
|
self.justify_map = self.justify_overrides() if self.justify else {}
|
2026-07-18 18:48:23 +02:00
|
|
|
self.make_cells(self.rows)
|
|
|
|
|
|
2026-07-24 21:37:58 +02:00
|
|
|
# Calculated cell values (:calc). A calculation is
|
|
|
|
|
# <target> = <op> <operand> ... (prefix operator: + - * /)
|
|
|
|
|
# and the SHAPE of the target selects the operation:
|
|
|
|
|
# * single cell r(c) -> FOLD: the operands collapse to one value.
|
|
|
|
|
# * row range R(c) -> horizontal MAP: run once per row in R.
|
|
|
|
|
# * column range r(C) -> vertical MAP: run once per column in C.
|
|
|
|
|
# In a map the target's ranged axis iterates; an operand's aligned axis
|
|
|
|
|
# iterates in lockstep and any other range in it folds. A relative
|
|
|
|
|
# operand omits the iterated axis ("(col)" in a row map; bare "rows" in a
|
|
|
|
|
# column map); a constant or a single fixed cell broadcasts. See
|
|
|
|
|
# notes/calc_notation.md. Calculations run in order, each reading values
|
|
|
|
|
# as displayed (display-precision); :format styles the results.
|
|
|
|
|
# Future operators to consider: min, max, mean.
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
|
2026-07-24 21:37:58 +02:00
|
|
|
# A single-axis selector: an index or an inclusive range; negatives count
|
|
|
|
|
# from the end (-1 = last). A target/operand token is <rows>(<cols>),
|
|
|
|
|
# <rows>, or (<cols>) -- the last two are the relative operand forms.
|
|
|
|
|
selector_rgx = re.compile(r"^(-?\d+)(-(-?\d+)?)?$")
|
|
|
|
|
operand_rgx = re.compile(
|
|
|
|
|
r"^(?P<rows>-?\d+(?:-(?:-?\d+)?)?)?(?:\((?P<cols>[-\d,]+)\))?$")
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
|
|
|
|
|
def calc_error(self, calc, message):
|
|
|
|
|
raise Exception(f'In the :calc calculation "{calc}": {message}')
|
|
|
|
|
|
2026-07-24 21:37:58 +02:00
|
|
|
def calc_warn(self, calc, message):
|
|
|
|
|
# Non-fatal: the value is still computed and stored (a covered target
|
|
|
|
|
# may be read as an operand by a later calc), it just is not rendered.
|
|
|
|
|
print(f'Warning: in the :calc calculation "{calc}": {message}',
|
|
|
|
|
file=sys.stderr)
|
|
|
|
|
|
|
|
|
|
def selector_error(self, ctx, message):
|
|
|
|
|
# ctx is the caller's prefix, e.g. 'In the :calc calculation "..."' or
|
|
|
|
|
# 'In :format "..."', so the same selector parser serves both.
|
|
|
|
|
raise Exception(f"{ctx}: {message}")
|
|
|
|
|
|
|
|
|
|
def to_number(self, text):
|
|
|
|
|
# A displayed cell value as a float, honoring :decimal and thousands
|
|
|
|
|
# separators; None (no error raised) when it is not a number.
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
s = text.strip()
|
|
|
|
|
if self.decimal == "comma":
|
|
|
|
|
s = s.translate(str.maketrans(",.", ".,"))
|
|
|
|
|
s = s.replace(",", "") # Remove thousands separators
|
|
|
|
|
try:
|
|
|
|
|
return float(s)
|
|
|
|
|
except ValueError:
|
2026-07-24 21:37:58 +02:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def parse_number(self, text, ref, calc):
|
|
|
|
|
v = self.to_number(text)
|
|
|
|
|
if v is None:
|
|
|
|
|
# Unescape KTESC markers so the message shows the character the
|
|
|
|
|
# writer typed (e.g. "$5") rather than "KTESC0024KTESC5".
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
self.calc_error(
|
2026-07-24 21:37:58 +02:00
|
|
|
calc, "the cell {} contains \"{}\", which is not a number"
|
|
|
|
|
.format(ref, klammer_base.unescape_ktesc(text.strip())))
|
|
|
|
|
return v
|
|
|
|
|
|
|
|
|
|
def format_number(self, value):
|
|
|
|
|
# Calc results are stored as plain numbers; :format does any styling.
|
|
|
|
|
s = str(int(value)) if value.is_integer() else str(value)
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
if self.decimal == "comma":
|
|
|
|
|
s = s.translate(str.maketrans(",.", ".,"))
|
|
|
|
|
return s
|
|
|
|
|
|
2026-07-24 21:37:58 +02:00
|
|
|
def calc_norm(self, i, count, spec, ctx):
|
|
|
|
|
# Resolve a possibly-negative index; -1 is the last, like Python.
|
|
|
|
|
j = i + count if i < 0 else i
|
|
|
|
|
if not 0 <= j < count:
|
|
|
|
|
self.selector_error(
|
|
|
|
|
ctx, f'in "{spec}", index {i} is out of range '
|
|
|
|
|
f"(0 through {count - 1})")
|
|
|
|
|
return j
|
|
|
|
|
|
|
|
|
|
def calc_selectors(self, spec, count, ctx):
|
|
|
|
|
# "3", "0-2", "0--2", "-1", or a comma-separated list of those, to a
|
|
|
|
|
# list of indices in written order (order matters for a left fold).
|
|
|
|
|
result = []
|
|
|
|
|
for part in spec.split(","):
|
|
|
|
|
m = self.selector_rgx.match(part)
|
|
|
|
|
if not m:
|
|
|
|
|
self.selector_error(ctx, f'"{part}" is not a valid selector')
|
|
|
|
|
start = self.calc_norm(int(m.group(1)), count, part, ctx)
|
|
|
|
|
if m.group(2) is None:
|
|
|
|
|
result.append(start)
|
|
|
|
|
continue
|
|
|
|
|
end = (self.calc_norm(int(m.group(3)), count, part, ctx)
|
|
|
|
|
if m.group(3) else count - 1)
|
|
|
|
|
if start > end:
|
|
|
|
|
self.selector_error(
|
|
|
|
|
ctx, f'in "{part}", the start {start} is after the end {end}')
|
|
|
|
|
result += list(range(start, end + 1))
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def cell_number(self, r, c, calc):
|
|
|
|
|
return self.parse_number(self.rows[r][c], f"{r}({c})", calc)
|
|
|
|
|
|
|
|
|
|
def parse_operand(self, token, calc):
|
|
|
|
|
# ('const', value) or ('cells', rows, cols) where each of rows/cols is
|
|
|
|
|
# a list of indices, or None when that axis is not written (a relative
|
|
|
|
|
# operand, resolved against the target's iterated axis by operand_cells).
|
|
|
|
|
try:
|
|
|
|
|
return ('const', float(token))
|
|
|
|
|
except ValueError:
|
|
|
|
|
pass
|
|
|
|
|
m = self.operand_rgx.match(token)
|
|
|
|
|
if not m or (m.group('rows') is None and m.group('cols') is None):
|
|
|
|
|
self.calc_error(
|
|
|
|
|
calc, f'"{token}" is not a number or a cell selection')
|
|
|
|
|
ctx = f'In the :calc calculation "{calc}"'
|
|
|
|
|
rows = (self.calc_selectors(m.group('rows'), self.row_count, ctx)
|
|
|
|
|
if m.group('rows') is not None else None)
|
|
|
|
|
cols = (self.calc_selectors(m.group('cols'), self.row_size, ctx)
|
|
|
|
|
if m.group('cols') is not None else None)
|
|
|
|
|
return ('cells', rows, cols)
|
|
|
|
|
|
|
|
|
|
def operand_cells(self, token, calc, mode, index, trange):
|
|
|
|
|
# The operand's numbers for the current target cell. mode is 'scalar',
|
|
|
|
|
# 'row' (horizontal map, rows iterate), or 'col' (vertical, cols
|
|
|
|
|
# iterate); index is the current row/col; trange is the target's range.
|
|
|
|
|
kind = self.parse_operand(token, calc)
|
|
|
|
|
if kind[0] == 'const':
|
|
|
|
|
return [kind[1]]
|
|
|
|
|
_, rows, cols = kind
|
|
|
|
|
if mode == 'scalar':
|
|
|
|
|
if rows is None or cols is None:
|
|
|
|
|
self.calc_error(
|
|
|
|
|
calc, f'"{token}" is a relative operand; it needs a ranged '
|
|
|
|
|
"target (a row or column range) to resolve against")
|
|
|
|
|
return [self.cell_number(r, c, calc) for r in rows for c in cols]
|
|
|
|
|
if mode == 'row': # rows iterate; any columns fold
|
|
|
|
|
if cols is None:
|
|
|
|
|
self.calc_error(
|
|
|
|
|
calc, f'"{token}" selects no column; a row-map operand '
|
|
|
|
|
"names a column, e.g. (0) or 0-(0)")
|
|
|
|
|
if rows is None: # relative: this row
|
|
|
|
|
use_rows = [index]
|
|
|
|
|
elif len(rows) == 1: # a fixed row broadcasts
|
|
|
|
|
use_rows = rows
|
|
|
|
|
elif rows == trange: # explicit range in lockstep
|
|
|
|
|
use_rows = [index]
|
|
|
|
|
else:
|
|
|
|
|
self.calc_error(
|
|
|
|
|
calc, f'the rows of "{token}" must match the target rows')
|
|
|
|
|
return [self.cell_number(r, c, calc) for r in use_rows for c in cols]
|
|
|
|
|
# mode == 'col': columns iterate; any rows fold
|
|
|
|
|
if rows is None:
|
|
|
|
|
self.calc_error(
|
|
|
|
|
calc, f'"{token}" selects no row; a column-map operand names '
|
|
|
|
|
"rows, e.g. 0--2 or 0--2(0-)")
|
|
|
|
|
if cols is None: # relative: this column
|
|
|
|
|
use_cols = [index]
|
|
|
|
|
elif len(cols) == 1: # a fixed column broadcasts
|
|
|
|
|
use_cols = cols
|
|
|
|
|
elif cols == trange: # explicit range in lockstep
|
|
|
|
|
use_cols = [index]
|
|
|
|
|
else:
|
|
|
|
|
self.calc_error(
|
|
|
|
|
calc, f'the columns of "{token}" must match the target columns')
|
|
|
|
|
return [self.cell_number(r, c, calc) for r in rows for c in use_cols]
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
|
|
|
|
|
def apply_operator(self, op, values, calc):
|
2026-07-24 21:37:58 +02:00
|
|
|
if len(values) == 1: # Lisp-style unary: - negates, / reciprocates
|
|
|
|
|
v = values[0] # + and * of one operand are the operand itself
|
|
|
|
|
if op == "-":
|
|
|
|
|
return -v
|
|
|
|
|
if op == "/": # compute 1/v only for "/", so "+ <zero cell>"
|
|
|
|
|
return 1 / v # does not raise a spurious ZeroDivisionError
|
|
|
|
|
return v
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
result = values[0]
|
|
|
|
|
for v in values[1:]: # Fold from the left
|
|
|
|
|
if op == "+":
|
|
|
|
|
result += v
|
|
|
|
|
elif op == "-":
|
|
|
|
|
result -= v
|
|
|
|
|
elif op == "*":
|
|
|
|
|
result *= v
|
|
|
|
|
else:
|
|
|
|
|
result /= v
|
|
|
|
|
return result
|
|
|
|
|
|
2026-07-24 21:37:58 +02:00
|
|
|
def calc_fold(self, op, operands, calc, mode, index, trange):
|
|
|
|
|
values = []
|
|
|
|
|
for token in operands:
|
|
|
|
|
values += self.operand_cells(token, calc, mode, index, trange)
|
|
|
|
|
try:
|
|
|
|
|
return self.apply_operator(op, values, calc)
|
|
|
|
|
except ZeroDivisionError:
|
|
|
|
|
self.calc_error(calc, "division by zero")
|
|
|
|
|
|
|
|
|
|
def calc_assign(self, r, c, value, calc, target_text):
|
|
|
|
|
if (r, c) in self.covered:
|
|
|
|
|
self.calc_warn(
|
|
|
|
|
calc, f"the target {target_text} is a cell hidden by a colspan "
|
|
|
|
|
"or rowspan merge; its computed value will not be shown")
|
|
|
|
|
self.rows[r][c] = self.format_number(value)
|
|
|
|
|
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
def calculate(self):
|
|
|
|
|
for calc in [c.strip() for c in self.calc.split(";") if c.strip()]:
|
2026-07-24 21:37:58 +02:00
|
|
|
self.run_calc(calc)
|
|
|
|
|
|
|
|
|
|
def run_calc(self, calc):
|
|
|
|
|
target, eq, expression = calc.partition("=")
|
|
|
|
|
target = target.strip()
|
|
|
|
|
m = self.operand_rgx.match(target)
|
|
|
|
|
if not eq or not m or m.group('rows') is None or m.group('cols') is None:
|
|
|
|
|
self.calc_error(
|
|
|
|
|
calc, "the target must be a cell r(c) or a ranged cell such as "
|
|
|
|
|
"0-(2) or -1(0-), followed by \"=\"")
|
|
|
|
|
ctx = f'In the :calc calculation "{calc}"'
|
|
|
|
|
trows = self.calc_selectors(m.group('rows'), self.row_count, ctx)
|
|
|
|
|
tcols = self.calc_selectors(m.group('cols'), self.row_size, ctx)
|
|
|
|
|
tokens = expression.split()
|
|
|
|
|
if not tokens or tokens[0] not in ("+", "-", "*", "/") or len(tokens) < 2:
|
|
|
|
|
self.calc_error(
|
|
|
|
|
calc, "the expression must be an operator (+ - * /) followed by "
|
|
|
|
|
"at least one operand")
|
|
|
|
|
op, operands = tokens[0], tokens[1:]
|
|
|
|
|
row_range, col_range = len(trows) > 1, len(tcols) > 1
|
|
|
|
|
if row_range and col_range:
|
|
|
|
|
self.calc_error(
|
|
|
|
|
calc, "the target may range over rows or columns, but not both")
|
|
|
|
|
if not row_range and not col_range: # single cell: a fold
|
|
|
|
|
v = self.calc_fold(op, operands, calc, 'scalar', None, None)
|
|
|
|
|
self.calc_assign(trows[0], tcols[0], v, calc, target)
|
|
|
|
|
elif row_range: # horizontal map
|
|
|
|
|
for r in trows:
|
|
|
|
|
v = self.calc_fold(op, operands, calc, 'row', r, trows)
|
|
|
|
|
self.calc_assign(r, tcols[0], v, calc, target)
|
|
|
|
|
else: # vertical map
|
|
|
|
|
for c in tcols:
|
|
|
|
|
v = self.calc_fold(op, operands, calc, 'col', c, tcols)
|
|
|
|
|
self.calc_assign(trows[0], c, v, calc, target)
|
|
|
|
|
|
|
|
|
|
# ---- :format ----------------------------------------------------------
|
|
|
|
|
#
|
|
|
|
|
# ";"-separated <cells> <function> pairs (same list style as :calc).
|
|
|
|
|
# <cells> is an indexed_range; <function> is a "<module>.<function>"
|
|
|
|
|
# reference (like an @eval reference) to a Python function taking
|
|
|
|
|
# (value, target) and returning the formatted cell text. Each selected
|
|
|
|
|
# cell's value is parsed as a number (honoring :decimal); if numeric the
|
|
|
|
|
# function is called and its result -- with the :decimal comma swap
|
|
|
|
|
# applied -- replaces the cell (e.g. a writer's myformats.euro function
|
|
|
|
|
# turns 1234.56 into "1,234.56 €"). A
|
|
|
|
|
# non-numeric cell is left as-is with a warning. Runs AFTER :calc. The
|
|
|
|
|
# result is inserted verbatim (this pass is after the cell-processing pass),
|
|
|
|
|
# so a function may emit target markup directly.
|
|
|
|
|
|
|
|
|
|
def format_warn(self, message):
|
|
|
|
|
print(f"Warning: in :format: {message}", file=sys.stderr)
|
|
|
|
|
|
|
|
|
|
def format_function(self, spec, ctx):
|
|
|
|
|
# Resolve "<module>.<function>" to a callable, like an @eval reference.
|
|
|
|
|
if "." not in spec:
|
|
|
|
|
self.selector_error(
|
|
|
|
|
ctx, f'the format function "{spec}" must be written '
|
|
|
|
|
"<module>.<function>, e.g. table.euro")
|
|
|
|
|
mod_name, func_name = spec.rsplit(".", 1)
|
|
|
|
|
try:
|
|
|
|
|
return getattr(importlib.import_module(mod_name), func_name)
|
|
|
|
|
except (ImportError, AttributeError):
|
|
|
|
|
self.selector_error(
|
|
|
|
|
ctx, f'the format function "{spec}" was not found')
|
|
|
|
|
|
|
|
|
|
def apply_formats(self):
|
|
|
|
|
for stmt in [s.strip() for s in self.format.split(";") if s.strip()]:
|
|
|
|
|
ctx = f'In :format "{stmt}"'
|
|
|
|
|
parts = stmt.split()
|
|
|
|
|
if len(parts) != 2:
|
|
|
|
|
self.selector_error(
|
|
|
|
|
ctx, "each entry is <cells> <function>, e.g. 0-(5) myformats.euro")
|
|
|
|
|
rangespec, spec = parts
|
|
|
|
|
func = self.format_function(spec, ctx)
|
|
|
|
|
m = self.operand_rgx.match(rangespec)
|
|
|
|
|
if not m or m.group('rows') is None or m.group('cols') is None:
|
|
|
|
|
self.selector_error(
|
|
|
|
|
ctx, f'"{rangespec}" is not a cell range like 0-(5) '
|
|
|
|
|
"or 1--2(0-3)")
|
|
|
|
|
rows = self.calc_selectors(m.group('rows'), self.row_count, ctx)
|
|
|
|
|
cols = self.calc_selectors(m.group('cols'), self.row_size, ctx)
|
|
|
|
|
for r in rows:
|
|
|
|
|
for c in cols:
|
|
|
|
|
v = self.to_number(self.rows[r][c])
|
|
|
|
|
if v is None:
|
|
|
|
|
self.format_warn(
|
|
|
|
|
f'cell {r}({c}) contains "'
|
|
|
|
|
f'{klammer_base.unescape_ktesc(self.rows[r][c].strip())}'
|
|
|
|
|
'", which is not a number; left unformatted')
|
|
|
|
|
continue
|
|
|
|
|
result = func(v, self.K_target)
|
|
|
|
|
if self.decimal == "comma":
|
|
|
|
|
result = result.translate(str.maketrans(",.", ".,"))
|
|
|
|
|
self.rows[r][c] = result
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
|
|
|
|
|
def span_count(self, spans, index, cross_i):
|
|
|
|
|
# The count of cells merged by a span anchored at (index, cross_i):
|
|
|
|
|
# for colspan, index is the row and cross_i the column; for rowspan,
|
|
|
|
|
# index is the column and cross_i the row. Non-anchor cells get 0.
|
2026-07-18 18:48:23 +02:00
|
|
|
result = 0
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
entry = spans[index]
|
|
|
|
|
if entry:
|
|
|
|
|
for start, end in entry.ranges:
|
|
|
|
|
if start == cross_i:
|
|
|
|
|
result = end - start + 1
|
2026-07-18 18:48:23 +02:00
|
|
|
return result
|
|
|
|
|
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
def compute_coverage(self):
|
|
|
|
|
# Cells hidden by a span (every spanned cell except the anchor).
|
|
|
|
|
self.colspan_covered = set()
|
|
|
|
|
self.rowspan_covered = set()
|
|
|
|
|
for row_i in self.s_colspan.by_index:
|
|
|
|
|
for start, end in self.s_colspan.by_index[row_i].ranges:
|
|
|
|
|
for col_i in range(start + 1, end + 1):
|
|
|
|
|
self.colspan_covered.add((row_i, col_i))
|
|
|
|
|
for col_i in self.s_rowspan.by_index:
|
|
|
|
|
for start, end in self.s_rowspan.by_index[col_i].ranges:
|
|
|
|
|
for row_i in range(start + 1, end + 1):
|
|
|
|
|
self.rowspan_covered.add((row_i, col_i))
|
|
|
|
|
self.covered = self.colspan_covered | self.rowspan_covered
|
|
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
def column_width_text(self):
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
# For each column, the text used to measure a 'fit' width in the
|
|
|
|
|
# tex target. The longest cell's font is applied, so a bold or
|
|
|
|
|
# italic cell is measured in the font it will be set in.
|
2026-07-18 18:48:23 +02:00
|
|
|
self.column_widths = []
|
|
|
|
|
for col_i in range(len(self.cells[0])):
|
|
|
|
|
longest = ""
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
longest_font = "r"
|
2026-07-18 18:48:23 +02:00
|
|
|
for row_i in range(len(self.cells)):
|
|
|
|
|
cell = self.cells[row_i][col_i]
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
# A colspan anchor's text spans several columns and must
|
|
|
|
|
# not set the width of its own column.
|
|
|
|
|
if cell is not None and cell.colspan <= 1:
|
|
|
|
|
lines = [e.strip() for e in cell.text.split("\\newline")]
|
2026-07-18 18:48:23 +02:00
|
|
|
lines = sorted(lines, key=len)
|
|
|
|
|
longest_in_line = lines[-1]
|
|
|
|
|
if len(longest_in_line) > len(longest):
|
|
|
|
|
longest = longest_in_line
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
longest_font = cell.font
|
|
|
|
|
if longest_font != "r":
|
|
|
|
|
longest = font.tex_fontify(longest, longest_font, 1.0)
|
2026-07-18 18:48:23 +02:00
|
|
|
self.column_widths.append(longest)
|
|
|
|
|
|
2026-07-24 21:37:58 +02:00
|
|
|
# ---- :hpos -------------------------------------------------------------
|
|
|
|
|
#
|
|
|
|
|
# ";"-separated <cells> <position> pairs (the same list style as :calc
|
|
|
|
|
# and :format). <cells> is an indexed_range; <position> is l, c, or r
|
|
|
|
|
# and overrides the column's :cell_hpos for the selected cells. A
|
|
|
|
|
# colspan anchor's override positions the whole merged cell; in the tex
|
|
|
|
|
# target an ordinary overridden cell is wrapped in \multicolumn{1}.
|
|
|
|
|
|
2026-07-25 21:16:21 +02:00
|
|
|
def justify_overrides(self):
|
2026-07-24 21:37:58 +02:00
|
|
|
result = {}
|
2026-07-25 21:16:21 +02:00
|
|
|
for stmt in [s.strip() for s in self.justify.split(";") if s.strip()]:
|
|
|
|
|
ctx = f'In :justify "{stmt}"'
|
2026-07-24 21:37:58 +02:00
|
|
|
parts = stmt.split()
|
|
|
|
|
if len(parts) != 2 or parts[1] not in ("l", "c", "r"):
|
|
|
|
|
self.selector_error(
|
|
|
|
|
ctx, "each entry is <cells> <position>, the position one "
|
|
|
|
|
"of l, c, or r -- e.g. -3--1(3) r")
|
|
|
|
|
rangespec, pos = parts
|
|
|
|
|
m = self.operand_rgx.match(rangespec)
|
|
|
|
|
if not m or m.group('rows') is None or m.group('cols') is None:
|
|
|
|
|
self.selector_error(
|
|
|
|
|
ctx, f'"{rangespec}" is not a cell range like 0-(5) '
|
|
|
|
|
"or 1--2(0-3)")
|
|
|
|
|
rows = self.calc_selectors(m.group('rows'), self.row_count, ctx)
|
|
|
|
|
cols = self.calc_selectors(m.group('cols'), self.row_size, ctx)
|
|
|
|
|
for r in rows:
|
|
|
|
|
for c in cols:
|
|
|
|
|
result[(r, c)] = pos
|
|
|
|
|
return result
|
|
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
def make_cells(self, rows):
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
self.compute_coverage()
|
2026-07-18 18:48:23 +02:00
|
|
|
result = []
|
|
|
|
|
cells = []
|
|
|
|
|
for row_i, row in enumerate(rows):
|
|
|
|
|
row_cells = []
|
|
|
|
|
for cell_i, cell in enumerate(row):
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
rspan = self.span_count(self.s_rowspan, cell_i, row_i)
|
2026-07-18 18:48:23 +02:00
|
|
|
cspan = self.span_count(self.s_colspan, row_i, cell_i)
|
|
|
|
|
font = self.font[cell_i]
|
|
|
|
|
if row_i == 0 and self.header:
|
|
|
|
|
font = self.header_font
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
# A span anchor's right and bottom borders come from the
|
|
|
|
|
# boundary at the END of the merged region.
|
|
|
|
|
right_i = cell_i + max(cspan, 1)
|
|
|
|
|
bottom_i = row_i + max(rspan, 1)
|
2026-07-25 21:16:21 +02:00
|
|
|
hpos = self.justify_map.get((row_i, cell_i), self.cell_hpos[cell_i])
|
2026-07-18 18:48:23 +02:00
|
|
|
row_cells.append(
|
|
|
|
|
table_cell.Cell(
|
|
|
|
|
cell,
|
2026-07-24 21:37:58 +02:00
|
|
|
font, hpos,
|
2026-07-18 18:48:23 +02:00
|
|
|
self.s_hline.has(row_i, cell_i),
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
self.s_vline.has(right_i, row_i),
|
|
|
|
|
self.s_hline.has(bottom_i, cell_i),
|
2026-07-18 18:48:23 +02:00
|
|
|
self.s_vline.has(cell_i, row_i),
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
self.s_vline.by_index.get(cell_i),
|
|
|
|
|
self.s_vline.by_index.get(right_i),
|
|
|
|
|
rspan, cspan,
|
2026-07-24 21:37:58 +02:00
|
|
|
first_column=(cell_i == 0),
|
2026-07-25 21:16:21 +02:00
|
|
|
hpos_forced=(row_i, cell_i) in self.justify_map,
|
|
|
|
|
fit_class=(self.fit_class
|
|
|
|
|
if cell_i in self.fit_columns else ""),
|
|
|
|
|
flush_left=(self.flush_l and cell_i == 0),
|
|
|
|
|
flush_right=(self.flush_r and
|
|
|
|
|
cell_i + max(cspan, 1) == self.row_size)))
|
2026-07-18 18:48:23 +02:00
|
|
|
cells.append(row_cells)
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
self.cells = cells
|
2026-07-18 18:48:23 +02:00
|
|
|
self.column_width_text()
|
|
|
|
|
|
|
|
|
|
# HTML
|
|
|
|
|
|
2026-07-25 21:16:21 +02:00
|
|
|
def html_colgroup(self):
|
|
|
|
|
# The CSS counterpart of tex_hpos()'s width resolution: 'fit' is the
|
|
|
|
|
# widest entry of the column and no larger (tex's \widthof — the
|
|
|
|
|
# cells' Wfit class clamps it there), a
|
|
|
|
|
# fraction is of the text column, and '*' shares the width the sized
|
|
|
|
|
# columns leave over. All-'fit' (the default) needs no markup at
|
|
|
|
|
# all. With no 'fit' column the fixed layout makes the fractions
|
|
|
|
|
# exact (long content wraps, as LaTeX's p{} columns do); a 'fit'
|
|
|
|
|
# column forces the auto layout, where the fraction widths are
|
|
|
|
|
# honored approximately.
|
|
|
|
|
widths = extend(self.column_width, self.row_size)
|
|
|
|
|
if all(w in ("fit", "f") for w in widths):
|
|
|
|
|
return "", "", None
|
|
|
|
|
fractions = sum(float(w) for w in widths if w not in ("fit", "f", "*"))
|
|
|
|
|
if any(w in ("fit", "f", "*") for w in widths):
|
|
|
|
|
table_width = "100%"
|
|
|
|
|
else:
|
|
|
|
|
table_width = f"{min(fractions, 1) * 100:g}%"
|
|
|
|
|
cols = ""
|
|
|
|
|
for w in widths:
|
|
|
|
|
if w in ("fit", "f"):
|
|
|
|
|
# Clamped to the widest entry by the cells' Wfit class
|
|
|
|
|
# (width 1% + nowrap; see table.css) -- a px width on the
|
|
|
|
|
# <col> does NOT survive surplus distribution when no '*'
|
|
|
|
|
# column exists (seen in both Firefox and Chrome).
|
|
|
|
|
cols += E("col").str()
|
|
|
|
|
elif w == "*":
|
|
|
|
|
cols += E("col").str()
|
|
|
|
|
else:
|
|
|
|
|
share = float(w) if table_width == "100%" else float(w) / fractions
|
|
|
|
|
cols += E("col").attr("style", f"width: {share * 100:g}%").str()
|
|
|
|
|
layout = "" if any(w in ("fit", "f") for w in widths) else "table-layout: fixed; "
|
|
|
|
|
# No blank line before </colgroup>: @document's insert_missing_ids
|
|
|
|
|
# would wrap it as a stray <p> inside the table.
|
|
|
|
|
return E("colgroup").body(cols.strip(), newline=False).str(), layout, table_width
|
|
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
def html(self):
|
|
|
|
|
result = ""
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
for row_i, row in enumerate(self.cells):
|
2026-07-18 18:48:23 +02:00
|
|
|
row_html = ""
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
for cell_i, cell in enumerate(row):
|
|
|
|
|
if (row_i, cell_i) in self.covered:
|
|
|
|
|
continue
|
2026-07-18 18:48:23 +02:00
|
|
|
row_html += cell.html().strip() + "\n"
|
|
|
|
|
result += E("tr").body(row_html).str()
|
2026-07-25 21:16:21 +02:00
|
|
|
if self.fill_columns:
|
|
|
|
|
# 'fill': the table sizes itself -- the browser's auto layout
|
|
|
|
|
# computes min(available, widest entries) natively, so the fill
|
|
|
|
|
# column grows only until nothing needs a line break. Several
|
|
|
|
|
# fill columns share in proportion to their content (the auto
|
|
|
|
|
# algorithm), matching the tex \ratio division. The max-width
|
|
|
|
|
# cap goes on the caption wrapper when there is one (the table's
|
|
|
|
|
# own percentage would be circular in a shrink-to-fit wrapper).
|
|
|
|
|
result = E("table").body(result)
|
|
|
|
|
if self.number or self.caption:
|
|
|
|
|
result = html_util.add_caption(
|
|
|
|
|
result, "Table", self.number, self.caption,
|
|
|
|
|
self.caption_font, hpos=self.hpos,
|
|
|
|
|
side=self.caption_side,
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
font_size=self.caption_font_size, max_width="100%",
|
|
|
|
|
offset=self.offset)
|
2026-07-25 21:16:21 +02:00
|
|
|
else:
|
|
|
|
|
result.attr("style", "max-width: 100%")
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
result = html_util.hpos_container(result, self.hpos, self.offset).str()
|
2026-07-25 21:16:21 +02:00
|
|
|
return result
|
|
|
|
|
colgroup, layout, table_width = self.html_colgroup()
|
|
|
|
|
result = E("table").body(colgroup + result)
|
2026-07-18 18:48:23 +02:00
|
|
|
if self.number or self.caption:
|
2026-07-25 21:16:21 +02:00
|
|
|
# The caption wrapper carries the width (a percentage on the
|
|
|
|
|
# shrink-to-fit wrapper itself would be circular) and the table
|
|
|
|
|
# fills it -- which also makes the caption track the table.
|
|
|
|
|
if table_width:
|
|
|
|
|
result.attr("style", f"{layout}width: 100%")
|
2026-07-18 18:48:23 +02:00
|
|
|
result = html_util.add_caption(
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
result, "Table", self.number, self.caption, self.caption_font,
|
2026-07-25 21:16:21 +02:00
|
|
|
hpos=self.hpos, side=self.caption_side,
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
font_size=self.caption_font_size, width=table_width,
|
|
|
|
|
offset=self.offset)
|
2026-07-18 18:48:23 +02:00
|
|
|
else:
|
2026-07-25 21:16:21 +02:00
|
|
|
# An uncaptioned table still gets the position container, so
|
|
|
|
|
# html and tex agree on where the table sits.
|
|
|
|
|
if table_width:
|
|
|
|
|
result.attr("style", f"{layout}width: {table_width}")
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
result = html_util.hpos_container(result, self.hpos, self.offset).str()
|
2026-07-18 18:48:23 +02:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# LaTeX
|
|
|
|
|
|
2026-07-25 21:16:21 +02:00
|
|
|
# The length registers holding computed 'fill' column widths, declared
|
|
|
|
|
# in table.sty; one per fill column, in column order.
|
|
|
|
|
fill_registers = ["\\klfilla", "\\klfillb", "\\klfillc", "\\klfilld"]
|
|
|
|
|
|
|
|
|
|
def tex_fill_widths(self):
|
|
|
|
|
# Set each 'fill' column's register to min(its share of the
|
|
|
|
|
# remaining width, its widest entry) -- the same rule the html auto
|
|
|
|
|
# layout applies. The shares divide the remaining width in
|
|
|
|
|
# proportion to the columns' widest entries (calc's \ratio): either
|
|
|
|
|
# the space covers them all and every column caps at its widest
|
|
|
|
|
# entry, or no column caps and all the space is used -- no stranded
|
|
|
|
|
# whitespace, and no iterative redistribution.
|
|
|
|
|
if not self.fill_columns:
|
|
|
|
|
return ""
|
|
|
|
|
widths = extend(self.column_width, self.row_size)
|
|
|
|
|
fit = [f"\\widthof{{{self.column_widths[i]}}}"
|
|
|
|
|
for i, w in enumerate(widths) if w in ("fit", "f")]
|
|
|
|
|
remaining = "\\tablewidth" + "".join(f" - {e}" for e in fit)
|
|
|
|
|
widest = {i: f"\\widthof{{{self.column_widths[i]}}}"
|
|
|
|
|
for i in self.fill_columns}
|
|
|
|
|
total = " + ".join(widest[i] for i in self.fill_columns)
|
|
|
|
|
result = ""
|
|
|
|
|
for k, i in enumerate(self.fill_columns):
|
|
|
|
|
reg = Table.fill_registers[k]
|
|
|
|
|
if len(self.fill_columns) == 1:
|
|
|
|
|
result += (f"\\setlength{{{reg}}}"
|
|
|
|
|
f"{{\\minof{{{remaining}}}{{{widest[i]}}}}}\n")
|
|
|
|
|
else:
|
|
|
|
|
result += (f"\\setlength{{{reg}}}{{({remaining})"
|
|
|
|
|
f"*\\ratio{{{widest[i]}}}{{{total}}}}}\n")
|
|
|
|
|
result += (f"\\setlength{{{reg}}}"
|
|
|
|
|
f"{{\\minof{{{reg}}}{{{widest[i]}}}}}\n")
|
|
|
|
|
return result
|
|
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
def tex_hpos(self):
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
# One column specification per column: the width comes from
|
2026-07-25 21:16:21 +02:00
|
|
|
# :column_width ('fit', 'fill' via its precomputed register, a
|
|
|
|
|
# fraction of \tablewidth, or '*' for the remaining width), the
|
|
|
|
|
# justification from :cell_hpos.
|
2026-07-18 18:48:23 +02:00
|
|
|
def par_format(s, justification):
|
|
|
|
|
command = {"l" : "raggedright",
|
|
|
|
|
"c" : "centering",
|
|
|
|
|
"r" : "raggedleft"}[justification]
|
|
|
|
|
return f">{{\\{command}}}p{{{s}}}"
|
|
|
|
|
|
|
|
|
|
widths = []
|
2026-07-25 21:16:21 +02:00
|
|
|
fill_ordinal = 0
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
for i, w in enumerate(extend(self.column_width, self.row_size)):
|
|
|
|
|
if w in ("fit", "f"):
|
|
|
|
|
widths.append(f"\\widthof{{{self.column_widths[i]}}}")
|
2026-07-25 21:16:21 +02:00
|
|
|
elif w == "fill":
|
|
|
|
|
widths.append(Table.fill_registers[fill_ordinal])
|
|
|
|
|
fill_ordinal += 1
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
elif w == "*":
|
|
|
|
|
widths.append(None)
|
2026-07-18 18:48:23 +02:00
|
|
|
else:
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
widths.append(f"{w}\\tablewidth")
|
|
|
|
|
fill_count = widths.count(None)
|
2026-07-18 18:48:23 +02:00
|
|
|
if fill_count > 0:
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
fixed = [e for e in widths if e is not None]
|
|
|
|
|
if fixed:
|
|
|
|
|
expr = f"(\\tablewidth - {' - '.join(fixed)}) / {fill_count}"
|
2026-07-18 18:48:23 +02:00
|
|
|
else:
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
expr = f"{1 / fill_count}\\tablewidth"
|
|
|
|
|
widths = [e if e is not None else expr for e in widths]
|
|
|
|
|
return [par_format(w, j) for w, j in zip(widths, self.cell_hpos)]
|
2026-07-18 18:48:23 +02:00
|
|
|
|
|
|
|
|
def tex_column_spec(self):
|
|
|
|
|
parts = [""] * (self.row_size * 2 + 1)
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
for i in self.s_vline.by_index:
|
2026-07-18 18:48:23 +02:00
|
|
|
parts[i * 2] = "|"
|
2026-07-25 21:16:21 +02:00
|
|
|
# A lineless table edge drops its outer \tabcolsep (matched by the
|
|
|
|
|
# \tablewidth arithmetic in tex()).
|
|
|
|
|
if self.flush_l:
|
|
|
|
|
parts[0] = "@{}"
|
|
|
|
|
if self.flush_r:
|
|
|
|
|
parts[-1] = "@{}"
|
2026-07-18 18:48:23 +02:00
|
|
|
for i, hpos in enumerate(self.tex_hpos()):
|
|
|
|
|
parts[i * 2 + 1] = hpos
|
|
|
|
|
# print("tex_column_spec:", "".join(parts))
|
|
|
|
|
return "".join(parts)
|
|
|
|
|
|
|
|
|
|
def tex_hline(self, index):
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
# Contiguous cell borders coalesce into single \cline runs; a
|
|
|
|
|
# full-width line becomes \hline.
|
2026-07-18 18:48:23 +02:00
|
|
|
bottom = index == self.row_count
|
|
|
|
|
if bottom:
|
|
|
|
|
index -= 1
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
flags = [(cell.border.bottom if bottom else cell.border.top)
|
|
|
|
|
for cell in self.cells[index]]
|
|
|
|
|
if not bottom:
|
|
|
|
|
# No line through the interior of a merged (rowspan) cell.
|
|
|
|
|
flags = [flag and (index, col_i) not in self.rowspan_covered
|
|
|
|
|
for col_i, flag in enumerate(flags)]
|
|
|
|
|
if flags and all(flags):
|
|
|
|
|
return "\\hline\n"
|
|
|
|
|
hline = ""
|
|
|
|
|
start = None
|
|
|
|
|
for i, flag in enumerate(flags + [False]):
|
|
|
|
|
if flag and start is None:
|
|
|
|
|
start = i
|
|
|
|
|
elif not flag and start is not None:
|
|
|
|
|
hline += f"\\cline{{{start + 1}-{i}}} "
|
|
|
|
|
start = None
|
2026-07-18 18:48:23 +02:00
|
|
|
return hline.strip() + "\n"
|
|
|
|
|
|
|
|
|
|
def tex_rows(self):
|
|
|
|
|
result = ""
|
|
|
|
|
for row_i, row in enumerate(self.cells):
|
|
|
|
|
result += self.tex_hline(row_i)
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
parts = []
|
2026-07-18 18:48:23 +02:00
|
|
|
for cell_i, cell in enumerate(row):
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
if (row_i, cell_i) in self.colspan_covered:
|
|
|
|
|
continue # Absorbed by the \multicolumn anchor
|
|
|
|
|
if (row_i, cell_i) in self.rowspan_covered:
|
|
|
|
|
parts.append("") # Occupied by the \multirow anchor
|
|
|
|
|
else:
|
|
|
|
|
parts.append(cell.tex())
|
|
|
|
|
result += " & ".join(parts) + " \\tabularnewline\n"
|
2026-07-18 18:48:23 +02:00
|
|
|
result += self.tex_hline(self.row_count)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def get_width(self):
|
|
|
|
|
box = "\\savebox{\\tablebox}{%\n"
|
|
|
|
|
box += "\\begin{tabular}{"
|
|
|
|
|
box += self.tex_column_spec()
|
|
|
|
|
box += "}\n"
|
|
|
|
|
box += self.tex_rows()
|
|
|
|
|
box += "\\end{tabular}}\n"
|
|
|
|
|
result = box + "\\setlength{\\tableboxwidth}{\\wd\\tablebox}\n"
|
|
|
|
|
# result += "\\the\\tableboxwidth\n\n"
|
|
|
|
|
# print(result)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def make_caption(self):
|
|
|
|
|
column_count = str(len(self.rows[0]))
|
|
|
|
|
caption = latex_util.make_caption_text(
|
|
|
|
|
self.number, "Table", self.caption, self.caption_font, self.caption_font_size)
|
|
|
|
|
result = "\\multicolumn{" + column_count + "}{c}{\\parbox{\\tableboxwidth}{"
|
|
|
|
|
result += "\\vspace*{8pt}\\centering\\small\\em "
|
|
|
|
|
result += caption
|
|
|
|
|
result += "}} \\\\ \\endlastfoot\n"
|
|
|
|
|
return result
|
|
|
|
|
|
2026-07-25 21:16:21 +02:00
|
|
|
def tex_position(self):
|
|
|
|
|
# Position a PAGE-BREAKING table with longtable's own glue (it
|
|
|
|
|
# cannot be boxed). A boxed table (allow_break false) is positioned
|
|
|
|
|
# by its :hpos wrapper instead; its glue is left neutral (\fill on
|
|
|
|
|
# both sides collapses in the exactly-fitting box), because a fixed
|
|
|
|
|
# length would overflow the box. A length is the left margin.
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
# :offset insets the table from the margin :hpos names -- the same
|
|
|
|
|
# rule the boxed path gets from latex_util.caption_wrapper, here
|
|
|
|
|
# expressed as the fixed side of the glue pair.
|
|
|
|
|
inset = latex_util.offset_length(self.offset)
|
2026-07-25 21:16:21 +02:00
|
|
|
if not self.allow_break:
|
|
|
|
|
left, right = "\\fill", "\\fill"
|
|
|
|
|
elif self.hpos == "center":
|
|
|
|
|
left, right = "\\fill", "\\fill"
|
|
|
|
|
elif self.hpos == "left":
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
left, right = inset, "\\fill"
|
2026-07-25 21:16:21 +02:00
|
|
|
elif self.hpos == "right":
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
left, right = "\\fill", inset
|
|
|
|
|
elif self.hpos == "none":
|
|
|
|
|
# No positioning container: the table starts at the text margin
|
|
|
|
|
# like ordinary text, which is what "none" does in html (the
|
|
|
|
|
# hpos_none class is inline-flex). A longtable cannot flow
|
|
|
|
|
# inline, so flush left is as close as the target gets. The
|
|
|
|
|
# offset names no margin here and is ignored, as when centered.
|
|
|
|
|
left, right = "0pt", "\\fill"
|
2026-07-25 21:16:21 +02:00
|
|
|
else:
|
|
|
|
|
length, _, _ = kutil.parse_length("tex", self.hpos, 1)
|
|
|
|
|
left, right = length, "\\fill"
|
|
|
|
|
return (f"\\setlength{{\\LTleft}}{{{left}}}\n"
|
|
|
|
|
f"\\setlength{{\\LTright}}{{{right}}}\n")
|
|
|
|
|
|
|
|
|
|
def tex_width_check(self, name):
|
|
|
|
|
# Emit a marker into the xelatex log when the measured table is
|
|
|
|
|
# wider than the text column (2pt tolerance for exactly-full-width
|
|
|
|
|
# tables). tex_to_pdf() in document.cpp scans the log for the
|
|
|
|
|
# marker and prints the console warning with the :column_width
|
|
|
|
|
# primer -- the widths are only known at LaTeX run time, and prose
|
|
|
|
|
# kept out of TeX avoids the log's 79-column line wrapping.
|
|
|
|
|
return ("\\ifdim\\tableboxwidth>\\dimexpr\\textwidth+2pt\\relax\n"
|
|
|
|
|
f"\\message{{^^JKT-WIDE-TABLE {name} overfull by "
|
|
|
|
|
"\\the\\dimexpr\\tableboxwidth-\\textwidth\\relax^^J}\n"
|
|
|
|
|
"\\fi\n")
|
|
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
def tex(self):
|
2026-07-25 21:16:21 +02:00
|
|
|
name = f"Reference-Table-{Table.id}"
|
|
|
|
|
Table.id += 1
|
|
|
|
|
# The measuring \savebox must stay OUTSIDE the \tableboxwidth
|
|
|
|
|
# minipage below: it computes the width the minipage consumes.
|
|
|
|
|
measure = self.get_width() + self.tex_width_check(name)
|
|
|
|
|
result = f"\\renewcommand*{{\\arraystretch}}{{{self.leading}}}\n"
|
2026-07-18 18:48:23 +02:00
|
|
|
if self.allow_break:
|
|
|
|
|
result += "\\vspace*{12pt}\n"
|
|
|
|
|
result += "\\begin{longtable}{"
|
|
|
|
|
result += self.tex_column_spec()
|
|
|
|
|
result += "}\n"
|
|
|
|
|
if self.allow_break:
|
|
|
|
|
result += self.make_caption()
|
|
|
|
|
result += self.tex_rows()
|
|
|
|
|
result += "\\end{longtable}\n"
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
|
2026-07-18 18:48:23 +02:00
|
|
|
if not self.allow_break:
|
2026-07-25 21:16:21 +02:00
|
|
|
# Box the table at its measured width so the caption tracks it
|
|
|
|
|
# and the box can be positioned as one piece; the LT glue then
|
|
|
|
|
# has no room and positioning falls to the :hpos wrapper. (The
|
|
|
|
|
# page-breaking table cannot be boxed -- there the LT glue
|
|
|
|
|
# positions and make_caption's \multicolumn tracks.)
|
|
|
|
|
result = latex_util.minipage(
|
|
|
|
|
result, "\\tableboxwidth", vertical="t", center=False)
|
2026-07-18 18:48:23 +02:00
|
|
|
if self.number or self.caption:
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
result = latex_util.add_caption(
|
2026-07-25 21:16:21 +02:00
|
|
|
result, "Table", self.number, self.caption, "\\tableboxwidth",
|
|
|
|
|
hpos=self.hpos, side=self.caption_side,
|
|
|
|
|
font_symbol=self.caption_font,
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
font_size=self.caption_font_size, offset=self.offset)
|
2026-07-18 18:48:23 +02:00
|
|
|
else:
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
result = latex_util.caption_wrapper(result, self.hpos, offset=self.offset)
|
2026-07-25 21:16:21 +02:00
|
|
|
result = measure + result
|
2026-07-18 18:48:23 +02:00
|
|
|
|
|
|
|
|
result = f"\\hypertarget{{{name}}}{{}}\\label{{Label-{name}}}\n{result}"
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
# The wrapper (add_caption/caption_wrapper) owns all vertical space
|
|
|
|
|
# around the table; longtable's own glue is zeroed.
|
|
|
|
|
result = (f"\\setlength{{\\tabcolsep}}{{{self.colsep}}}\n"
|
|
|
|
|
"\\setlength{\\LTpre}{0pt}\n"
|
|
|
|
|
"\\setlength{\\LTpost}{0pt}\n"
|
2026-07-25 21:16:21 +02:00
|
|
|
+ self.tex_position() +
|
|
|
|
|
# 2 \tabcolsep per column, minus the ones @{} removes at
|
|
|
|
|
# flush (lineless) edges.
|
|
|
|
|
f"\\setlength{{\\tablewidth}}{{\\textwidth - "
|
|
|
|
|
f"{2 * self.row_size - self.flush_l - self.flush_r}\\tabcolsep}}\n"
|
|
|
|
|
# Fill widths need \tablewidth and must precede the
|
|
|
|
|
# measuring \savebox, whose column spec reads them.
|
|
|
|
|
+ self.tex_fill_widths()
|
Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:
- Argument types end to end: :python_cast values are applied (Python
@eval receives real bools/numbers/lists), argument values are
validated against their argtype patterns with the argtype's
description as the error message, argtypes can declare :default
(overridable per declaration), and parameterized type families are
supported: rest(N) casts a rest argument to an N-dimensional list
(bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
:leading / :colsep wired, :colspan and :rowspan render (HTML
attributes; \multicolumn / \multirow), calculated cell values (:calc)
with prefix operators, display-precision semantics, :calc_format and
:decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
(infrastructure in mac/font_store; no Google Fonts links or fetch).
Default fonts live in the top-level fnt/; additional fonts install
into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
preview, install — classification by font metadata). CSS font family
names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
profiles source env/runtime.env. Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00
|
|
|
+ result)
|
2026-07-18 18:48:23 +02:00
|
|
|
result = re.sub(r"\newline", r"\\\\", result)
|
Option sets: a .o target for shared parameters
A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
2026-08-06 13:11:37 +02:00
|
|
|
# A boxed table is box material: it must sit in vertical mode or it
|
|
|
|
|
# is typeset beside any text it follows. (A page-breaking longtable
|
|
|
|
|
# breaks the paragraph itself, but \par on both sides is a no-op
|
|
|
|
|
# there, so the rule stays unconditional.)
|
|
|
|
|
return latex_util.block(result)
|
2026-07-18 18:48:23 +02:00
|
|
|
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
# This should signal that it has not be implemented by not being defined.
|
|
|
|
|
# def txt(self):
|
|
|
|
|
# return "Table in .txt format not implemented"
|
2026-07-18 18:48:23 +02:00
|
|
|
|