How can I calculate multi-line content spacing to set an equivalent text height?

I am trying to make the content in adjacent grid cells the same exact height.

If I remove paragraph leading, it works.

#set par(leading: 0em)
#grid(
  columns: 2,
  text(size: 30pt)[K],  // 3 lines * 10pt = 30pt
  text(size: 10pt)[K\ K\ K]
)

However, if I leave paragraph leading at the default, my calculation isn’t adding up correctly. The single-line cell text size is too small.

#set par(leading: 0.65em)
#grid(
  columns: 2,
  text(size: 43pt)[K],  // 3 lines * 10pt + 2 spaces * 0.65em * 10pt = 43pt
  text(size: 10pt)[K\ K\ K]
)

What am I missing?

Font size does not equal to height of K.

#set text(top-edge: "bounds", bottom-edge: "bounds")
#set text(font: "Liberation Sans")

#context {
  let cells = {
    let a = [K]
    let b = text(10pt)[K \ K \ K]
    let factor = measure(b).height / measure(a).height
    (text(factor * 1em, a), b)
    (scale(factor * 100%, reflow: true, a), b)
  }
  grid(columns: 2, ..cells)
}

#context grid(
  columns: 2, ..{
    let a = [K]
    let b = text(10pt)[K\ K\ K]
    let factor = measure(b).height / measure(a).height
    (text(factor * 1em, a), b)
    (scale(factor * 100%, reflow: true, a), b)
  }
)

measure depends on text.top-edge/text.bottom-edge.

I am not sure, if there is a neater way (If you do it with pt it works), but this also works

#set text(font: "Roboto Mono")

#let auto-fit(h-char, n-lines, h-line, first-char, body-text) = {
  context {
    set text(top-edge: "cap-height", bottom-edge: "baseline")
    let height-big = measure(text(size: h-char, first-char)).height
    let height-small = measure(text(size: h-line, "K")).height
    
    let calc-leading = (height-big - (n-lines * height-small)) / (n-lines - 1)
    grid(
      columns: 2,
      gutter: 0pt,
      text(size: h-char, first-char),
      block(above: 0pt, below: 0pt)[
        #set par(leading: calc-leading)
        #set text(size: h-line)
        #body-text
      ]
    )
  }
}

#grid(
  columns: 2,
  [
    #auto-fit(43pt, 3, 10pt, "K", [
K\
K\
K
])
  ],
  [
    #auto-fit(43pt, 3, 10pt, "Q", [
Q\
Q\
Q
])
  ]
)


Can someone explain in words? I’m not understanding the code-only solutions.

The edge settings aren’t changing anything for me.

#set par(leading: 0.65em)
#grid(
  columns: 2,
  text(size: 43pt, top-edge: "bounds", bottom-edge: "bounds")[K],
  text(size: 10pt, top-edge: "bounds", bottom-edge: "bounds")[K\ K\ K]
)

I’m only doing this once, so I want to keep the code simple and just hand calculate whatever text and spacing settings I need.

// #set text(top-edge: "bounds", bottom-edge: "bounds")
#show text: box.with(stroke: red + 0.1pt)

#context grid(
  columns: 2, ..{
    let a = [x]
    let b = text(10pt)[x\ x\ x]
    let factor = measure(b).height / measure(a).height
    (text(factor * 1em, a), b)
  }
)

#set text(top-edge: "bounds", bottom-edge: "bounds")
#show text: box.with(stroke: red + 0.1pt)

#context grid(
  columns: 2, ..{
    let a = [x]
    let b = text(10pt)[x\ x\ x]
    let factor = measure(b).height / measure(a).height
    (text(factor * 1em, a), b)
  }
)

The bounds are necessary for correctly measuring text bounding box, and laying out the result.

To calculate the height, just grab the factor value and paste it into the code:

#{
  set text(top-edge: "bounds", bottom-edge: "bounds")
  set text(font: "Liberation Sans")
  context {
    let a = [K]
    let b = text(10pt)[K \ K \ K]
    let factor = measure(b).height / measure(a).height
    repr(factor)
    grid(
      columns: 2,
      text(factor * 1em, a), b,
    )
  }
}

#{
  set text(font: "Liberation Sans", top-edge: "bounds", bottom-edge: "bounds")
  grid(
    columns: 2,
    text(4.445em)[K], text(10pt)[K \ K \ K],
  )
}

I don’t have bounds set in my packages, so probably you shouldn’t set it too, as it will change spacing between lines.

If sans-serif is the main font, then it can be hoisted, but inlining bounds will be longer than the example above.

#set text(font: "Liberation Sans")

#grid(
  columns: 2,
  text(4.445em, top-edge: "bounds", bottom-edge: "bounds")[K],
  text(10pt, top-edge: "bounds", bottom-edge: "bounds")[K \ K \ K],
)