Skip to content

clin_row_height(): add header pitch (the unshipped third of #97) #117

Description

@mstackhouse

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions