Universal text preparation and line layout with grapheme-aware wrapping, locale-aware segmentation, bidi support, glyph-run output, and pluggable text-measurement backends.
The core Pretext package targets netstandard2.0, net461, net6.0, net8.0, and net10.0. The Pretext.Uno companion package targets net10.0-desktop, and the native macOS sample host targets net10.0-macos.
PretextSharp is a .NET/C# port of the original pretext project by Cheng Lou.
Documentation site: wieslawsoltes.github.io/PretextSharp
Key documentation:
- Getting Started Overview
- Backend Discovery and Overrides
- Quickstart: Prepare and Layout
- Choosing an API
- Package: Pretext
- Package: Pretext.Layout
- Package: Pretext.Uno
- Prepared Text Lifecycle
- Public Types and Operations
- Rich Inline API
- Companion Helpers
- Prepare text once and reuse the result across repeated layout passes with
PrepareandPrepareWithSegments. - Compute fast aggregate metrics with
Layout, or materialize full line data withLayoutWithLines. - Stream line geometry incrementally with
LayoutNextLine,LayoutNextLineRange, andWalkLineRangesfor custom layout engines. - Re-materialize text lazily with
MaterializeLineRangeafter cheap geometry-only probing. - Measure widest-line geometry without allocating text via
MeasureLineStatsandMeasureNaturalWidth. - Handle ordinary spaces, preserved spaces, tabs, hard breaks, non-breaking spaces, zero-width breaks, and soft hyphens.
- Support
WordBreakMode.KeepAllfor CJK-focused no-space wrapping behavior. - Build rich inline flows with
PrepareRichInline,WalkRichInlineLineRanges, andMaterializeRichInlineLineRange. - Produce glyph-run output with
ShapeTextfor renderers that need glyph ids, positions, clusters, advances, and font runs. - Reuse prepared shaping with
ShapePreparedTextand shaped line materialization APIs for repeated wrapping/rendering passes. - Support multilingual text with locale-aware segmentation on desktop targets and bidi-aware segment levels.
- Keep the core library graphics-backend agnostic through
Pretext.Contracts. - Ship first-party native backends for Windows (
Pretext.DirectWrite), Linux (Pretext.FreeType), and macOS (Pretext.CoreText), plus the portablePretext.SkiaSharpfallback backend. - Ship with a published
Pretext.Layouthelper library for platform-neutral wrap and obstacle-layout workflows. - Ship with a published
Pretext.Unocompanion library for reusable Uno host controls and render scheduling helpers. - Ship with deterministic parity tests plus shared, Uno, and native macOS sample hosts that demonstrate bubbles, masonry, editorial, justification, rich-inline, and virtualized markdown chat layouts.
| API | Purpose |
|---|---|
Prepare |
Prepare text for repeated layout when you only need aggregate metrics. |
PrepareWithSegments |
Prepare text and expose segments, widths, break metadata, and segment levels. |
Layout |
Return line count and total height for a given width and line height. |
LayoutWithLines |
Return materialized line text and line widths. |
LayoutNextLine |
Stream the next line from a given cursor for custom layout flows. |
LayoutNextLineRange |
Stream geometry-only line ranges without materializing text. |
MaterializeLineRange |
Turn a LayoutLineRange back into a materialized LayoutLine. |
WalkLineRanges |
Iterate line geometry without allocating full line text. |
MeasureLineStats |
Return line count plus widest line width for a prepared block. |
MeasureNaturalWidth |
Return the widest unwrapped line width for prepared text. |
PrepareRichInline |
Prepare multi-item inline flow with collapsed boundary whitespace and atomic items. |
WalkRichInlineLineRanges |
Stream rich-inline line ranges without materializing fragment text. |
MaterializeRichInlineLineRange |
Materialize one streamed rich-inline line when you actually need fragment text. |
MeasureRichInlineStats |
Measure rich-inline line count and max line width. |
ShapeText |
Return glyph ids, positions, clusters, advances, and font runs for rendering. |
TryShapeText |
Attempt glyph-run output without throwing when no shaping backend is available. |
ShapePreparedText |
Cache shaping state for a prepared text object and reuse it while materializing shaped lines. |
LayoutNextShapedLine |
Stream shaped line output from prepared shaping state. |
MaterializeShapedLineRange |
Turn a LayoutLineRange into cached shaped glyph output. |
MaterializeShapedRichInlineLineRange |
Turn a rich-inline line range into cached shaped glyph output fragments. |
ProfilePrepare |
Measure preparation cost for profiling and diagnostics. |
SetLocale |
Override locale-sensitive segmentation behavior when needed. |
ClearCache |
Reset cached font state and prepared segment text caches. |
| Need | Start with |
|---|---|
| Line count and total height only | Prepare + Layout |
| Actual line text and widths | PrepareWithSegments + LayoutWithLines |
| One line at a time in a custom loop | PrepareWithSegments + LayoutNextLine |
| Geometry only, fewer allocations | PrepareWithSegments + WalkLineRanges |
| Rich inline fragments with atomic chips or badges | PrepareRichInline + WalkRichInlineLineRanges |
| Glyph ids and positions for custom rendering | ShapeText or TryShapeText |
| Repeated wrapping plus glyph rendering | PrepareWithSegments + ShapePreparedText + MaterializeShapedLineRange |
| Preparation cost diagnostics | ProfilePrepare |
Install the engine plus one or more backends:
dotnet add package Pretext
dotnet add package Pretext.SkiaSharpOptional host-native backends:
dotnet add package Pretext.DirectWrite # Windows
dotnet add package Pretext.FreeType # Linux
dotnet add package Pretext.CoreText # macOSSupported target frameworks for the core package:
netstandard2.0net461net6.0net8.0net10.0
Then prepare and lay out text:
using Pretext;
const string text = "Hello soft\u00ADwrapped world";
const string font = "16px Inter";
const double lineHeight = 20;
var prepared = PretextLayout.PrepareWithSegments(text, font);
var metrics = PretextLayout.Layout(prepared, maxWidth: 160, lineHeight);
var lines = PretextLayout.LayoutWithLines(prepared, maxWidth: 160, lineHeight);
Console.WriteLine(metrics.LineCount);
Console.WriteLine(metrics.Height);
foreach (var line in lines.Lines)
{
Console.WriteLine($"{line.Text} ({line.Width})");
}If the prepared text is empty after normalization, Layout returns new LayoutResult(0, 0). If a container in your UI must still reserve one visual row, clamp with Math.Max(1, metrics.LineCount) in the caller instead of expecting Pretext to synthesize a blank line.
The core package exposes the Pretext namespace and is not tied to Uno. Add one or more backend packages in non-Uno hosts so measurement can be provided automatically. When multiple first-party backends are referenced, Pretext prefers the host-native backend on its matching OS and falls back to Pretext.SkiaSharp otherwise.
The font argument is a CSS-like subset such as 16px Inter, italic 16px Georgia, or 700 18px "IBM Plex Sans". Line height is supplied separately to layout calls.
Use WhiteSpaceMode.PreWrap when your layout needs preserved spaces, tabs, or hard breaks:
var prepared = PretextLayout.PrepareWithSegments(
"foo\tbar\nbaz",
"16px Inter",
new PrepareOptions(WhiteSpaceMode.PreWrap));Use WordBreakMode.KeepAll when CJK-heavy text should avoid ordinary intra-run breaks:
var prepared = PretextLayout.PrepareWithSegments(
"日本語foo-bar",
"16px Inter",
new PrepareOptions(WordBreak: WordBreakMode.KeepAll));Use the rich-inline helper when paragraph text and atomic inline boxes must share one flow:
var flow = PretextLayout.PrepareRichInline(
[
new RichInlineItem("Ship ", "16px Inter"),
new RichInlineItem("@maya", "700 12px Inter", RichInlineBreakMode.Never, extraWidth: 18),
new RichInlineItem("'s note wraps cleanly.", "16px Inter"),
]);
PretextLayout.WalkRichInlineLineRanges(flow, 180, line =>
{
var materialized = PretextLayout.MaterializeRichInlineLineRange(flow, line);
Console.WriteLine(string.Join("", materialized.Fragments.Select(f => f.Text)));
});Use glyph-run output when a renderer wants to build its own positioned text blobs instead of calling backend string drawing APIs:
var shaped = PretextLayout.ShapeText("office", "16px Inter");
foreach (var glyph in shaped.Glyphs)
{
Console.WriteLine($"{glyph.GlyphId} at {glyph.X}, cluster {glyph.Cluster}");
}PretextGlyphRunKind.Shaped means the backend returned platform-shaped glyph positions. The current first-party shaped backends are Pretext.CoreText on macOS and Pretext.FreeType on Linux through native HarfBuzz. Pretext.FreeType falls back to PretextGlyphRunKind.Mapped if HarfBuzz is unavailable or the shaped primary-face run contains missing glyphs. Pretext.SkiaSharp returns PretextGlyphRunKind.Mapped, which is useful for simple glyph rendering but should not be treated as complex-script shaping.
For repeated layout/rendering loops, prepare once and reuse shaped line output by line range:
var prepared = PretextLayout.PrepareWithSegments("office file affine", "16px Inter");
var shapedPrepared = PretextLayout.ShapePreparedText(prepared);
var cursor = new LayoutCursor(0, 0);
while (PretextLayout.LayoutNextLineRange(prepared, cursor, 160) is { } line)
{
var shapedLine = PretextLayout.MaterializeShapedLineRange(shapedPrepared, line);
RenderGlyphs(shapedLine.ShapedRun.Glyphs);
cursor = line.End;
}ShapePreparedText keeps a prepared-object cache keyed by shaping direction. Whole-segment line ranges can be sliced from the prepared shaped run, while unsafe intra-segment breaks, soft-hyphen lines, and other context-sensitive ranges are shaped from the exact materialized line text and then cached by cursor range.
Install the platform-neutral layout-helper package when you want reusable wrap-metric and obstacle-flow helpers outside Uno as well:
dotnet add package Pretext.LayoutIt exposes:
Pretext.Layout.PreparedTextMetricsPretext.Layout.ColumnFlowLayoutPretext.Layout.ObstacleLayoutHelperPretext.Layout.WrapMetricsPretext.Layout.PositionedLine
Install the Uno companion package when you want the reusable Uno-specific helpers on top of the core engine:
dotnet add package Pretext.UnoIt brings Pretext, Pretext.Layout, Pretext.Contracts, Pretext.SkiaSharp, Pretext.DirectWrite, Pretext.FreeType, and Pretext.CoreText transitively, then lets backend discovery choose the best supported backend for the current OS. It exposes:
Pretext.PretextLayoutPretext.Layout.PreparedTextMetricsPretext.Layout.ColumnFlowLayoutPretext.Layout.ObstacleLayoutHelperPretext.Uno.Controls.StretchScrollHostPretext.Uno.Controls.UiRenderScheduler
The sample hosts share reusable data, prepared-model logic, and sample assets through samples/PretextSamples.Shared.
-
samples/PretextSamples.Unouses Uno Platform andPretext.Uno -
samples/PretextSamples.MacOSuses native AppKit onnet10.0-macosand bindsPretextexplicitly toPretext.CoreText -
Overview
-
Accordion
-
Bubbles
-
Masonry
-
Rich Text
-
Markdown Chat
-
Dynamic Layout
-
Editorial Engine
-
Justification Comparison
-
Variable ASCII
Run the Uno host with:
dotnet run --project samples/PretextSamples.Uno/PretextSamples.Uno.csproj -f net10.0-desktopRun the native macOS host with:
dotnet run --project samples/PretextSamples.MacOS/PretextSamples.MacOS.csproj -f net10.0-macosFor the current documentation map covering package selection, backend discovery, custom backends, and sample hosts, start in:
- .NET 10 SDK for building this repository
- Uno.Sdk 6.5.x for the Uno sample host only
dotnet build PretextSamples.slnx
dotnet build samples/PretextSamples.MacOS/PretextSamples.MacOS.csproj
dotnet test tests/Pretext.Uno.Tests/Pretext.Uno.Tests.csproj
bash ./pack-packages.shThe repository includes:
ci.ymlfor multi-platform build, test, docs validation, and preview package generationdocs.ymlfor GitHub Pages deploymentpack-packages.shfor the shared package restore/pack list used by CI and releasesrelease.ymlfor tag-driven packing, optional NuGet publication, and GitHub release creation- a Lunet docs site in
site/
The docs cover:
- installation and namespace/package selection
- font strings, measurement, and prepared-text lifecycle
- whitespace and break behavior, locale-aware segmentation, and bidi
- practical Uno, native host, and generic SkiaSharp integration patterns
- full reference coverage for the public core API and companion helper packages
src/
Pretext.Contracts/
Pretext/
Pretext.Layout/
Pretext.DirectWrite/
Pretext.FreeType/
Pretext.CoreText/
Pretext.SkiaSharp/
Pretext.Uno/
tests/
Pretext.Uno.Tests/
samples/
PretextSamples.Uno/
PretextSamples.Shared/
PretextSamples.MacOS/
site/
The core Pretext implementation in this repository is ported from the original pretext project by Cheng Lou. This repository adapts that work to .NET, native and SkiaSharp backends, packaging, tests, samples, and companion Uno helpers.
MIT. See LICENSE.