kb-text-layout: Multiline text measurement & layout.

[ bsd3, library, text ] [ Propose Tags ] [ Report a vulnerability ]

Modules

[Last Documentation]

  • KB
    • Text
      • Layout
        • KB.Text.Layout.Analysis
        • KB.Text.Layout.Break
        • KB.Text.Layout.Measure
        • KB.Text.Layout.Segmentation

Flags

Manual Flags

NameDescriptionDefault
demos

Build the demo executables and benchmarks.

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0, 0.1.0.1
Change log CHANGELOG.md
Dependencies base (>=4.20 && <5), bytestring, containers, hyphenation, kb-text-layout, kb-text-shape (>=0.2.1.0 && <0.3), text, vector, zstd [details]
License BSD-3-Clause
Copyright 2026 IC Rainbow
Author IC Rainbow
Maintainer aenor.realm@gmail.com
Uploaded by AlexanderBondarenko at 2026-08-15T18:46:18Z
Revised Revision 3 made by AlexanderBondarenko at 2026-08-16T00:22:28Z
Category Text
Source repo head: git clone https://gitlab.com/dpwiz/kb-text-layout
Distributions
Executables kb-text-layout-obstacles, kb-text-layout-masonry, kb-text-layout-justify, kb-text-layout-demo, kb-text-layout-bubbles
Downloads 1 total (1 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs not available [build log]
All reported builds failed as of 2026-08-15 [all 2 reports]

Readme for kb-text-layout-0.1.0.0

[back to package description]

kb-text-layout

Multiline text measurement & layout on top of kb-text-shape, inspired by pretext. Prepare a text once, then lay it out at any width.

import Data.Text.IO qualified as Text
import KB.Text.Layout.Break qualified as Break
import KB.Text.Layout.Measure qualified as Measure
import KB.Text.Shape qualified as KBTS

main :: IO ()
main =
  KBTS.withContext \shape -> do
    font <- KBTS.pushFontFromFile shape "demos/assets/Ubuntu-R.ttf" 0

    ctx <- Measure.createLayoutContext shape
    style <- Measure.newStyle ctx font 1.0
    prepared <- Measure.prepare ctx style "Soft hy\173phen\173ation and non\160breaking\160spaces."

    let maxWidth = 24
    let ranges = Break.layoutGreedy prepared maxWidth
    putStrLn $ "Stats: " <> show (Break.layoutStats ranges)
    putStrLn ""
    -- Stats: LayoutStats {lineCount = 2, maxLineWidth = 13.909091}

    putStrLn "Ranges:"
    mapM_ print ranges
    -- LineRange {from = Cursor {segment = 0, grapheme = 0}, to = Cursor {segment = 9, grapheme = 0}, width = 13.909091, ended = Wrapped}
    -- LineRange {from = Cursor {segment = 10, grapheme = 0}, to = Cursor {segment = 15, grapheme = 0}, width = 13.470421, ended = Finished}
    putStrLn ""

    putStrLn "Lines:"
    let laidout = map (Break.materializeLineRange prepared) ranges
    -- Soft hyphenation and
    -- non breaking spaces.
    mapM_ Text.putStrLn laidout

Everything after prepare is pure: relayout at another width is a fold over cached widths, with no shaper calls.

Units

To make sizes comparable across different fonts layout space is cap-height-normalized. newStyle ctx font 1.0 scales the font so a capital H is exactly 1.0 layout units tall. Style.em carries the em size in the same units for renderers that need CSS or pixel sizes.

Line height is a caller-chosen number of cap units. Ascenders and descenders overhang the fixed line box, so vertical font metrics never enter layout.

⚠️ Fonts without a cap-height metric fail at load time.

Demos

The demo executables live in demos/ behind the demos package flag (off by default).

  • demo: basic layout showing a ragged vs justified comparison.
  • masonry: packs a card corpus by shortest column.
  • justify: five columns of the same text at 300px.
    • Browser's own text-align: justify.
    • Greedy (fastest).
    • Greedy with soft hyphens from the hyphenation package.
    • layoutOptimal (slowest, for extra nice).
    • layoutOptimal plus hyphenation.
  • obstacles: Routes justified text around exclusion shapes.

Use make demos to run everything and rebuild the HTML pages.

Use stack bench to see the relative cost of each layout.

Limitations

  • Grapheme clusters come from kb-text-shape's KB.Text.Shape.Segmentation, so combining marks, Hangul jamo, flag pairs, and ZWJ emoji stay whole through emergency breaks and slicing. kbts skips UAX #29 GB11 and LB8a, so the wrapper refuses to cut, or break, adjacent to a ZWJ.
  • No dictionary-based segmenter for Thai/Lao/Khmer/Myanmar; no algorithmic breaker provides one.
  • Spans are shaped whole, so widths reflect joined and kerned forms. At a chosen break, a line that ends mid-join renders letterforms whose widths differ slightly from the joined measurement; a re-shape refinement pass does not exist yet.
  • Pretext's preprocessing rules are largely subsumed by kbts line breaking. Analysis adds the two missing tailorings: URL query-separator splits scoped to slash-containing tokens, and en/em-dash digit-range suppression.
  • prepare needs the open TextShape.Context; the PreparedText does not. Prepared texts can be laid out, sliced, and emergency-broken after the context closes.
  • pushFontFromFile and pushFontFromMemory reject missing files and fonts without unitsPerEm or a cap-height metric at load time, so bad fonts fail fast.