katom types from ref/ in groups, @table :leading in lines of text and :rulesep, @code under txt, one table style attribute, literal-klammer options (from dev 2e453744f5e9)

kdesc --katoms is Klammertext rendered from ref/, the types in titled groups,
with --katoms full for the regex column.  @table :leading L is L lines of the
surrounding text in html as in pdf (default 1), and :rulesep spaces the
horizontal rules.  The html table has one style attribute, so :column_width
fractions take effect again.  @code and @source_listing render in plain
text; captions are numbered per kind there, and a table needs no blank line
around it.  @c takes :nd, and an option-shaped word before a bar inside
literal code is code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ackandClaude Fable 5.1 committed 2026-09-21 22:37:13 +02:00
1 parent 63956c00e5
commit 217c0f3fc5
23 files changed
+625 -290

No files matched your search

+14
View File
@@ -58,8 +58,21 @@ td {
--cell-border: 1px black solid;
}
/* The space between a rule and the text on either side of it: a cell is
padded on the side that CARRIES the rule, so a row with no rule is
untouched, a partial :hline pads only the cells it crosses, and the
table's outer rules are padded on the inner side alone. The amounts
began as booktabs' defaults (0.4ex above a rule, 0.65ex below one) and
were tuned by eye, 2026-09-20: half of the space below moved above, so
0.725ex above and 0.325ex below, the total unchanged. The tex target's
pair is in table.py (TEX_RULE_ABOVE/_BELOW) and differs on purpose -- a
TeX row and an html row distribute their own height differently around
the text. --rulesep is @table's :rulesep, a multiplier, set on the
<table> by table.py. "ex" is the x-height as RENDERED
(font-size-adjust), so the space tracks the visible type. */
.Bt {
border-top: var(--cell-border);
padding-top: calc(var(--rulesep, 1) * 0.325ex);
}
.Br {
@@ -68,6 +81,7 @@ td {
.Bb {
border-bottom: var(--cell-border);
padding-bottom: calc(var(--rulesep, 1) * 0.725ex);
}
.Bl {
+11 -1
View File
@@ -213,7 +213,8 @@
:calc.table_calc
:format.table_format
:decimal.decimal_mark
:leading.float 1.3
:leading.float 1.0
:rulesep.float 1
:colsep 4pt
:
A table of rows of cells. The cells of a row are separated by "|" and the
@@ -226,6 +227,15 @@ draw lines, ^:colspan and ^:rowspan merge cells, ^:justify and ^:cell_hpos
place text within them, ^:calc computes cells from other cells and ^:format
formats them, and ^:hpos and ^:offset place the whole table in the text
column.
^:leading is the height of a row in lines of the surrounding text, the same
in html and pdf. ^:rulesep is the space between a horizontal line and the
text on either side of it, as a multiple of the standard amount (slightly
more below a line than above it); 0 sets the lines against the rows. The
lines at the top and the bottom of the table are spaced on their inner side
only. A table with vertical lines gets no such space in a pdf, where it
would break them. Neither argument has a meaning in plain text, where a row
is a line of characters and a horizontal line already has a line to itself.
@@
@@table.html,tex,txt :: @eval table.Table(K) eval@ @@
+77 -29
View File
@@ -575,23 +575,39 @@ class Table(klammer_base.Klammer_base):
# would wrap it as a stray <p> inside the table.
return E("colgroup").body(cols.strip(), newline=False).str(), layout, table_width
def html_style(self):
# :leading and :colsep under html (2026-09-18; both were tex-only).
# \arraystretch multiplies the row strut, which is \baselineskip,
# 1.2 times the font size -- so :leading L is a 1.2 L em row, and
# the html equivalent is that line-height on the table, inherited
# by every cell. The cells' vertical padding went with it
# (table.css): an html-only extra with no tex counterpart, which
# kept a :leading 1 table 0.3rem looser per row than its pdf.
# :colsep is a custom property the td rule reads for its horizontal
# padding (padding does not inherit); the flush-edge classes still
# zero the outer ones. Measured 2026-09-18: rows of :leading 1 and
# :leading 2 in the ratio 1:2 in both targets.
line_height = f"{1.2 * float(self.leading):.4g}"
def html_style(self, layout=""):
# The WHOLE style attribute of the <table>, written once: "layout" is
# the width declarations the caller has worked out (max-width for a
# fill table, table-layout/width for fractions). E.attr() appends,
# so a second .attr("style", ...) made a second attribute, which a
# browser discards -- from 2026-09-18, when this function arrived,
# to 2026-09-20 every width declaration was lost that way.
# :leading and :colsep under html. ":leading L" is L times the pitch
# of a line of the surrounding TEXT, in every target. tex has that
# by construction: \arraystretch multiplies the row strut, which is
# the \baselineskip in force. The html counterpart of \baselineskip
# is --text_line_height (font.css, 1.5), so the table's line-height
# is L times that, inherited by every cell. It was 1.2 L from
# 2026-09-18 to 09-20, LaTeX's ratio of \baselineskip to font size:
# wrong in html, where font-size-adjust draws the type larger than
# the nominal size line-height is computed from, so a :leading 1
# table sat 16% tighter than the paragraphs around it (measured:
# 20.2px rows beside 24px text, where the pdf had 14.2pt beside
# 14.2pt). The fallback serves a fragment without the stylesheet.
# The cells have no vertical padding (table.css): an html-only extra
# with no tex counterpart. :colsep is a custom property the td rule
# reads for its horizontal padding (padding does not inherit); the
# flush-edge classes still zero the outer ones.
line_height = f"calc({float(self.leading):.4g} * var(--text_line_height, 1.5))"
# Hidden: the result is re-read and the html target's transforms
# would turn the property's "--" into an en dash (font.py's trap).
return klammer_base.hide_typographic(
f"line-height: {line_height}; --colsep: {self.colsep}")
# :rulesep is a custom property too: the .Bt/.Bb rules (table.css)
# read it for the padding on the side of a cell that carries a rule.
style = (f"line-height: {line_height}; --colsep: {self.colsep}; "
f"--rulesep: {float(self.rulesep):.4g}")
if layout:
style += "; " + layout.strip().rstrip(";")
return klammer_base.hide_typographic(style)
def html(self):
result = ""
@@ -610,8 +626,10 @@ class Table(klammer_base.Klammer_base):
# 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).attr("style", self.html_style())
if self.number or self.caption:
captioned = self.number or self.caption
result = E("table").body(result).attr(
"style", self.html_style("" if captioned else "max-width: 100%"))
if captioned:
result = html_util.add_caption(
result, "Table", self.number, self.caption,
self.caption_style, hpos=self.hpos,
@@ -619,17 +637,19 @@ class Table(klammer_base.Klammer_base):
font_size=self.caption_size, max_width="100%",
offset=self.offset)
else:
result.attr("style", "max-width: 100%")
result = html_util.hpos_container(result, self.hpos, self.offset).str()
return result
colgroup, layout, table_width = self.html_colgroup()
result = E("table").body(colgroup + result).attr("style", self.html_style())
if self.number or self.caption:
captioned = self.number or self.caption
table_layout = ""
if table_width:
table_layout = f"{layout}width: " + ("100%" if captioned else table_width)
result = E("table").body(colgroup + result).attr(
"style", self.html_style(table_layout))
if captioned:
# 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%")
result = html_util.add_caption(
result, "Table", self.number, self.caption, self.caption_style,
hpos=self.hpos, side=self.caption_side,
@@ -638,8 +658,6 @@ class Table(klammer_base.Klammer_base):
else:
# 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}")
result = html_util.hpos_container(result, self.hpos, self.offset).str()
return result
@@ -761,9 +779,32 @@ class Table(klammer_base.Klammer_base):
# print("tex_column_spec:", "".join(parts))
return "".join(parts)
TEX_RULE_ABOVE = 0.4 # ex; booktabs' \aboverulesep
TEX_RULE_BELOW = 0.35 # 468 # ex; booktabs' \belowrulesep (0.65) less 20%, less 10%
def tex_rule_separations(self, top, bottom):
# The space above and below a rule, as booktabs' \specialrule and
# \cmidrule take them: :rulesep times TEX_RULE_ABOVE/_BELOW. They
# began as booktabs' defaults (0.4ex above a rule, 0.65ex below) and
# were tuned by eye, per target, 2026-09-20 (Andy): the amounts that
# LOOK alike differ, because a TeX row carries its strut's depth
# under the text and an html row its half-leading. The html pair is
# in table.css (.Bt/.Bb). The table's outer rules are padded on the
# INNER side only.
rulesep = float(self.rulesep)
above = "0pt" if top else f"{self.TEX_RULE_ABOVE * rulesep:.4g}ex"
below = "0pt" if bottom else f"{self.TEX_RULE_BELOW * rulesep:.4g}ex"
return above, below
def tex_hline(self, index):
# Contiguous cell borders coalesce into single \cline runs; a
# full-width line becomes \hline.
# Contiguous cell borders coalesce into single rule runs. The rules
# are booktabs' -- an \hline sits flush against the strut of the
# rows on either side, and the only way to open space there is a
# strut in every neighbouring row -- EXCEPT in a table with vertical
# lines: a booktabs rule is set between rows, so it breaks every
# vertical line it crosses (its author provides for none), and such
# a table keeps \hline/\cline and gets no space at its rules.
top = index == 0
bottom = index == self.row_count
if bottom:
index -= 1
@@ -773,7 +814,11 @@ class Table(klammer_base.Klammer_base):
# 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)]
spaced = not self.s_vline.by_index and float(self.rulesep) > 0
above, below = self.tex_rule_separations(top, bottom)
if flags and all(flags):
if spaced:
return f"\\specialrule{{\\arrayrulewidth}}{{{above}}}{{{below}}}\n"
return "\\hline\n"
hline = ""
start = None
@@ -781,7 +826,10 @@ class Table(klammer_base.Klammer_base):
if flag and start is None:
start = i
elif not flag and start is not None:
hline += f"\\cline{{{start + 1}-{i}}} "
if spaced:
hline += f"\\cmidrule[\\arrayrulewidth]{{{start + 1}-{i}}} "
else:
hline += f"\\cline{{{start + 1}-{i}}} "
start = None
return hline.strip() + "\n"
@@ -1063,7 +1111,7 @@ class Table(klammer_base.Klammer_base):
if len(rows) in self.s_hline.by_index:
result += rule
result = txt_util.add_caption(
result, "Table", self.number, self.caption, self.caption_side, \
int(self.Target_txt_indent))
result, "Table", self.number, self.caption, self.caption_side,
len(indent) // len(txt_util.NBSP))
return result