Paragraph layout: hyphenation, line breaking and justification.
This is the part Tincture inherits most directly from Joe Armstrong's erlguten, and the reason the library exists rather than wrapping a browser.
Line breaking
Two strategies, chosen with the :line_break option:
:greedy— fill each line as far as it goes, then break. Fast, and what most PDF libraries do.:optimal— Knuth-Plass. Considers the paragraph as a whole and minimises total badness, so a tight line early on can be relaxed to avoid a much worse one later. This is what TeX does, and it is visibly better in justified text.
The optimal breaker models text as boxes, glue and penalties, and scores candidate breaks on fitness class, hyphenation, consecutive hyphens, and widows and orphans — all tunable per call.
Hyphenation
TeX hyphenation patterns, for :en_gb, :da_dk, :fi_fi, :nb_no and
:sv_se. See Tincture.Typography.Hyphen.
Example
rich = RichText.from_plain(body, font: "Times-Roman", size: 11)
Typography.layout_paragraph(rich, 450,
align: :justified,
line_break: :optimal,
widow_penalty: 150,
orphan_penalty: 150
)Returns positioned Line structs. Most callers reach this through
Tincture.text_paragraph/6 rather than calling it directly.
Summary
Types
@type align() :: :left | :center | :right | :justified
@type line_break_mode() :: :greedy | :optimal
@type optimal_cost_model() :: :quadratic | :box_glue
@type option() :: {:align, align()} | {:line_height, number()} | {:line_break, line_break_mode()} | {:optimal_cost_model, optimal_cost_model()} | {:justify_max_space_multiplier, number()} | {:justify_min_space_multiplier, number()} | {:widow_penalty, number()} | {:orphan_penalty, number()} | {:hyphen_penalty, number()} | {:fitness_class_penalty, number()} | {:consecutive_hyphen_penalty, number()}
Functions
@spec layout_paragraph(Tincture.Typography.RichText.t(), number(), [option()]) :: [ Tincture.Typography.Line.t() ]
@spec layout_paragraph_with_spill( Tincture.Typography.RichText.t(), number(), pos_integer(), [option()] ) :: Tincture.Typography.LayoutResult.t()