KiteTextLine
class KiteTextLine(val text: String, val bounds: KiteRectangle, val charEdges: DoubleArray, val vertical: Boolean = false)
One laid-out line. charEdges has text.length + 1 display-space boundaries: charEdges[i] is where char i starts, the final entry where the line ends. That is enough to build sub-line highlight quads. Entries follow the text's logical order, so right-to-left runs may have descending edges and positioned runs may change direction. Hit tests use each adjacent pair's extent; highlights enclose every selected pair.
They are x boundaries on a normal line. On a vertical one (Japanese tategaki, where a "line" is a column running down the page) they are y boundaries instead, and bounds gives the column's width.