Motivation
This is the unshipped third of #97 — "configurable row/header pitch + header buffer/gap + rule placement." Two of the three landed and the pilot uses both:
| #97 asked for |
shipped as |
pilot |
| row pitch |
clin_row_height() (#100) |
adopted — one central call |
| header buffer / gap |
clin_header_pad() (#100/#102), completed by #113 |
adoptable now that per-row values work |
| header pitch |
— |
still a raw flextable::line_spacing(part = "header") |
clin_row_height() owns vertical pitch for the body, titles and footnotes, but there is no lever on the header, which is where this suite's hardest fidelity problem lives. Multi-line header cells (an arm label wrapped over three lines, "Change\nfrom Bsln\nMean (SD)", "Fisher's Exact\np-values") render with looser leading than the reference RTFs' ~13pt/line, and tightening them is only reachable outside the clinify API:
# tables/t_14_4_01.R — per-table styler override, purely to tighten header leading
flextable::line_spacing(x, space = 0.75, part = "header")
Current behavior
library(clinify); library(flextable)
df <- data.frame(stub = c("Male","Female"), a = c("5 (10%)","7 (14%)"))
mk <- function(f = identity) {
ct <- clintable(df, use_labels = FALSE) |>
clin_column_headers(stub = "", a = c("Xanomeline\nLow Dose\n(N=84)", "n (%)"))
f(ct)
}
probe <- function(ct, lbl) {
x <- clinify:::finish_table_(ct)
cat(sprintf("%-50s header line_spacing=%s body rowheights=%s\n", lbl,
paste(unique(as.vector(x$header$styles$pars$line_spacing$data)), collapse=","),
paste(round(unique(x$body$rowheights),3), collapse=",")))
}
probe(mk(), "plain")
probe(mk(\(c) clin_row_height(c, body = 15.35, title = 11.4, footnote = 11.4,
rule = "atleast", unit = "pt")), "clin_row_height(body=15.35, ...)")
probe(mk(\(c) flextable::line_spacing(c, space = 0.75, part = "header")), "flextable::line_spacing(part='header')")
Observed:
plain header line_spacing=1 body rowheights=0.25
clin_row_height(body=15.35, ...) header line_spacing=1 body rowheights=0.213
flextable::line_spacing(part='header') header line_spacing=0.75 body rowheights=0.25
clin_row_height() moves the body pitch and leaves the header alone; the header is only reachable through the raw flextable call.
Why it matters more than it looks
I added a header line-structure check to the pilot's fidelity harness this week (prompted by your side-note on #112 — thank you, it was the most useful thing in that thread). It compares the ordered lines of the header block, which neither a pixel diff nor a whitespace-stripped text-set comparison can see.
Result: 9 of 30 tables have multi-line header cells that stack differently from the reference. For example 14-1.02 puts p-value on header line 5 where the reference has it on line 6 — the same text, distributed across header lines differently. That class of difference was invisible to every gate before, and header leading is the lever that would address it.
So this isn't only tidiness: it's the one remaining vertical-pitch control the suite can't state through clinify, and it maps onto a measurable fidelity gap.
Proposed
Extend clin_row_height() with the header, in the same units and with the same rule semantics as the rest:
clin_row_height(x, body = 15.35, title = 11.4, footnote = 11.4,
header = 13, # <- new
rule = "atleast", unit = "pt")
One design question I'd leave to you, since the two levers differ for headers:
- row height (what
body/title/footnote set) bounds the whole header row, and with rule = "atleast" a 3-line cell still grows — which is the safe behavior;
- line leading (what
line_spacing sets) is what actually tightens the gap between lines inside a multi-line header cell, and is what 14-4.01 needs.
A header = height alone may not reach the multi-line case, so if only one ships I'd favor the leading control (or both, e.g. header + header_line). Naming and shape entirely yours — the ask is that header pitch be expressible through the clinify API rather than a raw flextable::line_spacing(part = "header") in every project that cares.
Related: #87 (whitespace gaps between spanner rules) is the horizontal-rule half of the same header-spacing area.
Motivation
This is the unshipped third of #97 — "configurable row/header pitch + header buffer/gap + rule placement." Two of the three landed and the pilot uses both:
clin_row_height()(#100)clin_header_pad()(#100/#102), completed by #113flextable::line_spacing(part = "header")clin_row_height()owns vertical pitch for the body, titles and footnotes, but there is no lever on the header, which is where this suite's hardest fidelity problem lives. Multi-line header cells (an arm label wrapped over three lines,"Change\nfrom Bsln\nMean (SD)","Fisher's Exact\np-values") render with looser leading than the reference RTFs' ~13pt/line, and tightening them is only reachable outside the clinify API:Current behavior
Observed:
clin_row_height()moves the body pitch and leaves the header alone; the header is only reachable through the raw flextable call.Why it matters more than it looks
I added a header line-structure check to the pilot's fidelity harness this week (prompted by your side-note on #112 — thank you, it was the most useful thing in that thread). It compares the ordered lines of the header block, which neither a pixel diff nor a whitespace-stripped text-set comparison can see.
Result: 9 of 30 tables have multi-line header cells that stack differently from the reference. For example 14-1.02 puts
p-valueon header line 5 where the reference has it on line 6 — the same text, distributed across header lines differently. That class of difference was invisible to every gate before, and header leading is the lever that would address it.So this isn't only tidiness: it's the one remaining vertical-pitch control the suite can't state through clinify, and it maps onto a measurable fidelity gap.
Proposed
Extend
clin_row_height()with the header, in the same units and with the samerulesemantics as the rest:One design question I'd leave to you, since the two levers differ for headers:
body/title/footnoteset) bounds the whole header row, and withrule = "atleast"a 3-line cell still grows — which is the safe behavior;line_spacingsets) is what actually tightens the gap between lines inside a multi-line header cell, and is what 14-4.01 needs.A
header =height alone may not reach the multi-line case, so if only one ships I'd favor the leading control (or both, e.g.header+header_line). Naming and shape entirely yours — the ask is that header pitch be expressible through the clinify API rather than a rawflextable::line_spacing(part = "header")in every project that cares.Related: #87 (whitespace gaps between spanner rules) is the horizontal-rule half of the same header-spacing area.