{"id":49389843,"url":"https://github.com/wieslawsoltes/pretextsharp","last_synced_at":"2026-05-01T21:00:27.505Z","repository":{"id":350930974,"uuid":"1208825405","full_name":"wieslawsoltes/PretextSharp","owner":"wieslawsoltes","description":"Universal text preparation and line layout for any SkiaSharp-based UI, with grapheme-aware wrapping, locale-aware segmentation, bidi support, and streaming line walking.","archived":false,"fork":false,"pushed_at":"2026-04-28T10:23:41.000Z","size":1995,"stargazers_count":17,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-28T12:34:31.450Z","etag":null,"topics":["pretext","rich-text","skiasharp","text-rendering","uno"],"latest_commit_sha":null,"homepage":"https://wieslawsoltes.github.io/PretextSharp/","language":"CSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wieslawsoltes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-12T19:43:16.000Z","updated_at":"2026-04-28T10:18:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wieslawsoltes/PretextSharp","commit_stats":null,"previous_names":["wieslawsoltes/pretextsharp"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/wieslawsoltes/PretextSharp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FPretextSharp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FPretextSharp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FPretextSharp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FPretextSharp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wieslawsoltes","download_url":"https://codeload.github.com/wieslawsoltes/PretextSharp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FPretextSharp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32512670,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pretext","rich-text","skiasharp","text-rendering","uno"],"created_at":"2026-04-28T12:04:36.300Z","updated_at":"2026-05-01T21:00:27.491Z","avatar_url":"https://github.com/wieslawsoltes.png","language":"CSS","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pretext\n\n[![CI](https://github.com/wieslawsoltes/PretextSharp/actions/workflows/ci.yml/badge.svg)](https://github.com/wieslawsoltes/PretextSharp/actions/workflows/ci.yml)\n[![NuGet](https://img.shields.io/nuget/v/Pretext.svg)](https://www.nuget.org/packages/Pretext)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Pretext.svg)](https://www.nuget.org/packages/Pretext)\n[![Docs](https://img.shields.io/badge/docs-github%20pages-0f766e)](https://wieslawsoltes.github.io/PretextSharp/)\n![Targets](https://img.shields.io/badge/targets-netstandard2.0%20%7C%20net461%20%7C%20net6.0%20%7C%20net8.0%20%7C%20net10.0-512BD4)\n![SkiaSharp 3.119.1](https://img.shields.io/badge/SkiaSharp-3.119.1-16A34A)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nUniversal text preparation and line layout with grapheme-aware wrapping, locale-aware segmentation, bidi support, and pluggable text-measurement backends.\n\nThe 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`.\n\nPretextSharp is a .NET/C# port of the original [pretext](https://github.com/chenglou/pretext) project by [Cheng Lou](https://github.com/chenglou).\n\nDocumentation site: [wieslawsoltes.github.io/PretextSharp](https://wieslawsoltes.github.io/PretextSharp/)\n\nKey documentation:\n\n- [Getting Started Overview](https://wieslawsoltes.github.io/PretextSharp/articles/getting-started/overview/)\n- [Backend Discovery and Overrides](https://wieslawsoltes.github.io/PretextSharp/articles/guides/backend-discovery-and-overrides/)\n- [Quickstart: Prepare and Layout](https://wieslawsoltes.github.io/PretextSharp/articles/getting-started/quickstart-prepare-and-layout/)\n- [Choosing an API](https://wieslawsoltes.github.io/PretextSharp/articles/getting-started/choosing-an-api/)\n- [Package: Pretext](https://wieslawsoltes.github.io/PretextSharp/articles/reference/package-pretext/)\n- [Package: Pretext.Layout](https://wieslawsoltes.github.io/PretextSharp/articles/reference/package-pretext-layout/)\n- [Package: Pretext.Uno](https://wieslawsoltes.github.io/PretextSharp/articles/reference/package-pretext-uno/)\n- [Prepared Text Lifecycle](https://wieslawsoltes.github.io/PretextSharp/articles/concepts/prepared-text-lifecycle/)\n- [Public Types and Operations](https://wieslawsoltes.github.io/PretextSharp/articles/reference/public-types-and-operations/)\n- [Rich Inline API](https://wieslawsoltes.github.io/PretextSharp/articles/reference/rich-inline-api/)\n- [Companion Helpers](https://wieslawsoltes.github.io/PretextSharp/articles/reference/pretext-uno-helpers/)\n\n## NuGet Packages\n\n| Package Name | NuGet | Downloads | Description |\n| --- | --- | --- | --- |\n| `Pretext` | [![NuGet](https://img.shields.io/nuget/v/Pretext.svg)](https://www.nuget.org/packages/Pretext) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Pretext.svg)](https://www.nuget.org/packages/Pretext) | Backend-agnostic text preparation and line layout engine. |\n| `Pretext.Contracts` | [![NuGet](https://img.shields.io/nuget/v/Pretext.Contracts.svg)](https://www.nuget.org/packages/Pretext.Contracts) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Pretext.Contracts.svg)](https://www.nuget.org/packages/Pretext.Contracts) | Contracts for implementing custom text-measurement backends. |\n| `Pretext.Layout` | [![NuGet](https://img.shields.io/nuget/v/Pretext.Layout.svg)](https://www.nuget.org/packages/Pretext.Layout) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Pretext.Layout.svg)](https://www.nuget.org/packages/Pretext.Layout) | Platform-neutral wrap and obstacle-layout helpers built on top of `Pretext`. |\n| `Pretext.DirectWrite` | [![NuGet](https://img.shields.io/nuget/v/Pretext.DirectWrite.svg)](https://www.nuget.org/packages/Pretext.DirectWrite) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Pretext.DirectWrite.svg)](https://www.nuget.org/packages/Pretext.DirectWrite) | First-party DirectWrite backend for Windows hosts. |\n| `Pretext.FreeType` | [![NuGet](https://img.shields.io/nuget/v/Pretext.FreeType.svg)](https://www.nuget.org/packages/Pretext.FreeType) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Pretext.FreeType.svg)](https://www.nuget.org/packages/Pretext.FreeType) | First-party FreeType + Fontconfig backend for Linux hosts. |\n| `Pretext.CoreText` | [![NuGet](https://img.shields.io/nuget/v/Pretext.CoreText.svg)](https://www.nuget.org/packages/Pretext.CoreText) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Pretext.CoreText.svg)](https://www.nuget.org/packages/Pretext.CoreText) | First-party CoreText backend for macOS hosts. |\n| `Pretext.SkiaSharp` | [![NuGet](https://img.shields.io/nuget/v/Pretext.SkiaSharp.svg)](https://www.nuget.org/packages/Pretext.SkiaSharp) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Pretext.SkiaSharp.svg)](https://www.nuget.org/packages/Pretext.SkiaSharp) | First-party SkiaSharp backend for `Pretext`. |\n| `Pretext.Uno` | [![NuGet](https://img.shields.io/nuget/v/Pretext.Uno.svg)](https://www.nuget.org/packages/Pretext.Uno) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Pretext.Uno.svg)](https://www.nuget.org/packages/Pretext.Uno) | Uno-specific controls and render scheduling helpers built on top of `Pretext`. |\n\n## Features\n\n- Prepare text once and reuse the result across repeated layout passes with `Prepare` and `PrepareWithSegments`.\n- Compute fast aggregate metrics with `Layout`, or materialize full line data with `LayoutWithLines`.\n- Stream line geometry incrementally with `LayoutNextLine`, `LayoutNextLineRange`, and `WalkLineRanges` for custom layout engines.\n- Re-materialize text lazily with `MaterializeLineRange` after cheap geometry-only probing.\n- Measure widest-line geometry without allocating text via `MeasureLineStats` and `MeasureNaturalWidth`.\n- Handle ordinary spaces, preserved spaces, tabs, hard breaks, non-breaking spaces, zero-width breaks, and soft hyphens.\n- Support `WordBreakMode.KeepAll` for CJK-focused no-space wrapping behavior.\n- Build rich inline flows with `PrepareRichInline`, `WalkRichInlineLineRanges`, and `MaterializeRichInlineLineRange`.\n- Support multilingual text with locale-aware segmentation on desktop targets and bidi-aware segment levels.\n- Keep the core library graphics-backend agnostic through `Pretext.Contracts`.\n- Ship first-party native backends for Windows (`Pretext.DirectWrite`), Linux (`Pretext.FreeType`), and macOS (`Pretext.CoreText`), plus the portable `Pretext.SkiaSharp` fallback backend.\n- Ship with a published `Pretext.Layout` helper library for platform-neutral wrap and obstacle-layout workflows.\n- Ship with a published `Pretext.Uno` companion library for reusable Uno host controls and render scheduling helpers.\n- 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.\n\n## Core API\n\n| API | Purpose |\n| --- | --- |\n| `Prepare` | Prepare text for repeated layout when you only need aggregate metrics. |\n| `PrepareWithSegments` | Prepare text and expose segments, widths, break metadata, and segment levels. |\n| `Layout` | Return line count and total height for a given width and line height. |\n| `LayoutWithLines` | Return materialized line text and line widths. |\n| `LayoutNextLine` | Stream the next line from a given cursor for custom layout flows. |\n| `LayoutNextLineRange` | Stream geometry-only line ranges without materializing text. |\n| `MaterializeLineRange` | Turn a `LayoutLineRange` back into a materialized `LayoutLine`. |\n| `WalkLineRanges` | Iterate line geometry without allocating full line text. |\n| `MeasureLineStats` | Return line count plus widest line width for a prepared block. |\n| `MeasureNaturalWidth` | Return the widest unwrapped line width for prepared text. |\n| `PrepareRichInline` | Prepare multi-item inline flow with collapsed boundary whitespace and atomic items. |\n| `WalkRichInlineLineRanges` | Stream rich-inline line ranges without materializing fragment text. |\n| `MaterializeRichInlineLineRange` | Materialize one streamed rich-inline line when you actually need fragment text. |\n| `MeasureRichInlineStats` | Measure rich-inline line count and max line width. |\n| `ProfilePrepare` | Measure preparation cost for profiling and diagnostics. |\n| `SetLocale` | Override locale-sensitive segmentation behavior when needed. |\n| `ClearCache` | Reset cached font state and prepared segment text caches. |\n\n## API Selection\n\n| Need | Start with |\n| --- | --- |\n| Line count and total height only | `Prepare` + `Layout` |\n| Actual line text and widths | `PrepareWithSegments` + `LayoutWithLines` |\n| One line at a time in a custom loop | `PrepareWithSegments` + `LayoutNextLine` |\n| Geometry only, fewer allocations | `PrepareWithSegments` + `WalkLineRanges` |\n| Rich inline fragments with atomic chips or badges | `PrepareRichInline` + `WalkRichInlineLineRanges` |\n| Preparation cost diagnostics | `ProfilePrepare` |\n\n## Quick Start\n\nInstall the engine plus one or more backends:\n\n```bash\ndotnet add package Pretext\ndotnet add package Pretext.SkiaSharp\n```\n\nOptional host-native backends:\n\n```bash\ndotnet add package Pretext.DirectWrite   # Windows\ndotnet add package Pretext.FreeType      # Linux\ndotnet add package Pretext.CoreText      # macOS\n```\n\nSupported target frameworks for the core package:\n\n- `netstandard2.0`\n- `net461`\n- `net6.0`\n- `net8.0`\n- `net10.0`\n\nThen prepare and lay out text:\n\n```csharp\nusing Pretext;\n\nconst string text = \"Hello soft\\u00ADwrapped world\";\nconst string font = \"16px Inter\";\nconst double lineHeight = 20;\n\nvar prepared = PretextLayout.PrepareWithSegments(text, font);\nvar metrics = PretextLayout.Layout(prepared, maxWidth: 160, lineHeight);\nvar lines = PretextLayout.LayoutWithLines(prepared, maxWidth: 160, lineHeight);\n\nConsole.WriteLine(metrics.LineCount);\nConsole.WriteLine(metrics.Height);\n\nforeach (var line in lines.Lines)\n{\n    Console.WriteLine($\"{line.Text} ({line.Width})\");\n}\n```\n\nIf 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.\n\nThe 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.\n\nThe `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.\n\nUse `WhiteSpaceMode.PreWrap` when your layout needs preserved spaces, tabs, or hard breaks:\n\n```csharp\nvar prepared = PretextLayout.PrepareWithSegments(\n    \"foo\\tbar\\nbaz\",\n    \"16px Inter\",\n    new PrepareOptions(WhiteSpaceMode.PreWrap));\n```\n\nUse `WordBreakMode.KeepAll` when CJK-heavy text should avoid ordinary intra-run breaks:\n\n```csharp\nvar prepared = PretextLayout.PrepareWithSegments(\n    \"日本語foo-bar\",\n    \"16px Inter\",\n    new PrepareOptions(WordBreak: WordBreakMode.KeepAll));\n```\n\nUse the rich-inline helper when paragraph text and atomic inline boxes must share one flow:\n\n```csharp\nvar flow = PretextLayout.PrepareRichInline(\n[\n    new RichInlineItem(\"Ship \", \"16px Inter\"),\n    new RichInlineItem(\"@maya\", \"700 12px Inter\", RichInlineBreakMode.Never, extraWidth: 18),\n    new RichInlineItem(\"'s note wraps cleanly.\", \"16px Inter\"),\n]);\n\nPretextLayout.WalkRichInlineLineRanges(flow, 180, line =\u003e\n{\n    var materialized = PretextLayout.MaterializeRichInlineLineRange(flow, line);\n    Console.WriteLine(string.Join(\"\", materialized.Fragments.Select(f =\u003e f.Text)));\n});\n```\n\n## Companion Packages\n\nInstall the platform-neutral layout-helper package when you want reusable wrap-metric and obstacle-flow helpers outside Uno as well:\n\n```bash\ndotnet add package Pretext.Layout\n```\n\nIt exposes:\n\n- `Pretext.Layout.PreparedTextMetrics`\n- `Pretext.Layout.ColumnFlowLayout`\n- `Pretext.Layout.ObstacleLayoutHelper`\n- `Pretext.Layout.WrapMetrics`\n- `Pretext.Layout.PositionedLine`\n\n## Uno Companion\n\nInstall the Uno companion package when you want the reusable Uno-specific helpers on top of the core engine:\n\n```bash\ndotnet add package Pretext.Uno\n```\n\nIt 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:\n\n- `Pretext.PretextLayout`\n- `Pretext.Layout.PreparedTextMetrics`\n- `Pretext.Layout.ColumnFlowLayout`\n- `Pretext.Layout.ObstacleLayoutHelper`\n- `Pretext.Uno.Controls.StretchScrollHost`\n- `Pretext.Uno.Controls.UiRenderScheduler`\n\n## Sample Apps\n\nThe sample hosts share reusable data, prepared-model logic, and sample assets through `samples/PretextSamples.Shared`.\n\n- `samples/PretextSamples.Uno` uses Uno Platform and `Pretext.Uno`\n- `samples/PretextSamples.MacOS` uses native AppKit on `net10.0-macos` and binds `Pretext` explicitly to `Pretext.CoreText`\n\n- Overview\n- Accordion\n- Bubbles\n- Masonry\n- Rich Text\n- Markdown Chat\n- Dynamic Layout\n- Editorial Engine\n- Justification Comparison\n- Variable ASCII\n\nRun the Uno host with:\n\n```bash\ndotnet run --project samples/PretextSamples.Uno/PretextSamples.Uno.csproj -f net10.0-desktop\n```\n\nRun the native macOS host with:\n\n```bash\ndotnet run --project samples/PretextSamples.MacOS/PretextSamples.MacOS.csproj -f net10.0-macos\n```\n\nFor the current documentation map covering package selection, backend discovery, custom backends, and sample hosts, start in:\n\n- [Installation](https://wieslawsoltes.github.io/PretextSharp/articles/getting-started/installation/)\n- [Backend Discovery and Overrides](https://wieslawsoltes.github.io/PretextSharp/articles/guides/backend-discovery-and-overrides/)\n- [Sample Hosts and Shared Assets](https://wieslawsoltes.github.io/PretextSharp/articles/guides/sample-hosts-and-shared-assets/)\n\n## Building\n\n### Prerequisites\n\n- .NET 10 SDK for building this repository\n- Uno.Sdk 6.5.x for the Uno sample host only\n\n### Build, test, and pack\n\n```bash\ndotnet build PretextSamples.slnx\ndotnet build samples/PretextSamples.MacOS/PretextSamples.MacOS.csproj\ndotnet test tests/Pretext.Uno.Tests/Pretext.Uno.Tests.csproj\nbash ./pack-packages.sh\n```\n\n## Docs and CI\n\nThe repository includes:\n\n- `ci.yml` for multi-platform build, test, docs validation, and preview package generation\n- `docs.yml` for GitHub Pages deployment\n- `pack-packages.sh` for the shared package restore/pack list used by CI and releases\n- `release.yml` for tag-driven packing, optional NuGet publication, and GitHub release creation\n- a Lunet docs site in `site/`\n\nThe docs cover:\n\n- installation and namespace/package selection\n- font strings, measurement, and prepared-text lifecycle\n- whitespace and break behavior, locale-aware segmentation, and bidi\n- practical Uno, native host, and generic SkiaSharp integration patterns\n- full reference coverage for the public core API and companion helper packages\n\n## Project Structure\n\n```text\nsrc/\n  Pretext.Contracts/\n  Pretext/\n  Pretext.Layout/\n  Pretext.DirectWrite/\n  Pretext.FreeType/\n  Pretext.CoreText/\n  Pretext.SkiaSharp/\n  Pretext.Uno/\ntests/\n  Pretext.Uno.Tests/\nsamples/\n  PretextSamples.Uno/\n  PretextSamples.Shared/\n  PretextSamples.MacOS/\nsite/\n```\n\n## Attribution\n\nThe core Pretext implementation in this repository is ported from the original [pretext](https://github.com/chenglou/pretext) project by [Cheng Lou](https://github.com/chenglou). This repository adapts that work to .NET, native and SkiaSharp backends, packaging, tests, samples, and companion Uno helpers.\n\n## License\n\nMIT. See [LICENSE](https://github.com/wieslawsoltes/PretextSharp/blob/main/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwieslawsoltes%2Fpretextsharp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwieslawsoltes%2Fpretextsharp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwieslawsoltes%2Fpretextsharp/lists"}