Meziantou.Framework.Unicode 2.0.7

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Meziantou.Framework.Unicode --version 2.0.7
                    
NuGet\Install-Package Meziantou.Framework.Unicode -Version 2.0.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Meziantou.Framework.Unicode" Version="2.0.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Meziantou.Framework.Unicode" Version="2.0.7" />
                    
Directory.Packages.props
<PackageReference Include="Meziantou.Framework.Unicode" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Meziantou.Framework.Unicode --version 2.0.7
                    
#r "nuget: Meziantou.Framework.Unicode, 2.0.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Meziantou.Framework.Unicode@2.0.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Meziantou.Framework.Unicode&version=2.0.7
                    
Install as a Cake Addin
#tool nuget:?package=Meziantou.Framework.Unicode&version=2.0.7
                    
Install as a Cake Tool

Meziantou.Framework.Unicode

This package provides Unicode text algorithms and data that do not depend on the Unicode version of the runtime or on ICU: character properties, grapheme, word and line segmentation, normalization, the bidirectional algorithm, script runs, and confusable detection. The data is the one of Unicode 18.0.0.

using Meziantou.Framework;

var input = "раураl"; // Uses Cyrillic letters that look like Latin
var normalized = Unicode.ReplaceConfusablesCharacters(input);

Console.WriteLine(normalized); // "paypal"

Detecting confusable strings

To decide whether two strings look alike, use the UTS #39 skeleton algorithm rather than comparing the output of ReplaceConfusablesCharacters:

Unicode.AreConfusable("paypal", "раураl"); // true  (Cyrillic look-alikes)
Unicode.AreConfusable("café", "café");     // true  (composed vs decomposed)
Unicode.AreConfusable("paypal", "example");     // false

Unicode.GetConfusableSkeleton(string) exposes the key itself, so it can be stored or indexed to find collisions across a set of names:

var skeleton = Unicode.GetConfusableSkeleton(userName);

The skeleton is a comparison key, not displayable text. Do not show it to users or store it in place of the original string.

These two methods use the normalization of this package (see Normalization), not the one of the platform: the skeleton does not depend on the version of ICU and is available in globalization-invariant mode (InvariantGlobalization=true).

Detecting mixed-script text

A homograph attack usually shows up as a string that mixes writing systems. IsMixedScript implements the UTS #39 resolved-script-set rules:

Unicode.IsMixedScript("paypal");   // false - all Latin
Unicode.IsMixedScript("раураl");   // true  - Cyrillic letters with a Latin "l"
Unicode.IsMixedScript("日本語です"); // false - Japanese legitimately mixes Han and Hiragana
Unicode.IsMixedScript("user123");  // false - digits match any script

Script data is also exposed directly:

UnicodeScripts.GetScript(new Rune('A'));      // UnicodeScript.Latin
UnicodeScripts.GetScript(new Rune(0x0410));   // UnicodeScript.Cyrillic
UnicodeScripts.GetIso15924Code(UnicodeScript.Latin); // "Latn"

// Script_Extensions: the scripts a shared character is used with
Span<UnicodeScript> scripts = stackalloc UnicodeScript[UnicodeScripts.MaxScriptExtensionCount];
var count = UnicodeScripts.GetScriptExtensions(0x30FC, scripts); // Hiragana, Katakana
UnicodeScripts.HasScriptExtension(0x30FC, UnicodeScript.Katakana); // true

Character properties

UnicodeProperties gives the properties of a code point that text processing needs: segmentation, bidirectional ordering, shaping and case. Every method takes an int, never throws and never allocates: the value is read from a table indexed by the code point.

UnicodeProperties.GetGeneralCategory('A');           // UppercaseLetter
UnicodeProperties.GetCanonicalCombiningClass(0x0301); // 230
UnicodeProperties.GetBidiClass(0x05D0);              // RightToLeft
UnicodeProperties.TryGetBidiMirroringGlyph('(', out var mirrored); // true, ')'
UnicodeProperties.GetLineBreakClass(0x00A0);         // Glue
UnicodeProperties.GetGraphemeClusterBreak(0x200D);   // ZWJ
UnicodeProperties.GetWordBreak('_');                 // ExtendNumLet
UnicodeProperties.GetEastAsianWidth(0x4E00);         // Wide
UnicodeProperties.GetJoiningType(0x0628);            // DualJoining
UnicodeProperties.GetIndicSyllabicCategory(0x094D);  // Virama
UnicodeProperties.IsDefaultIgnorable(0x00AD);        // true
UnicodeProperties.ToSimpleLowercase('A');            // 'a'
  • The values are those of one version of the Unicode Standard, UnicodeProperties.UnicodeVersion (18.0.0), whatever the version the runtime implements. The results are the same on every platform and every framework version, and do not need ICU.
  • An unassigned code point has the default value the standard gives to its range, for instance RightToLeft in the Hebrew block. An integer that is not a code point has the default value of the property.
  • The numeric values of the enumerations (UnicodeScript, UnicodeLineBreakClass, ...) never change: a later version of the standard only appends values.

Text segmentation

The boundaries are those of UAX #29 and UAX #14 for Unicode 18.0.0. The enumerators are ref structs over a ReadOnlySpan<char> and do not allocate.

// Extended grapheme clusters: what a reader perceives as one character
foreach (Range cluster in Unicode.EnumerateGraphemeClusters("e\u0301🇫🇷"))
{
    // "e\u0301", then the flag
}

// Word boundaries: words, numbers, runs of spaces, punctuation marks
foreach (Range segment in Unicode.EnumerateWordBoundaries("Hello, world!"))
{
    // "Hello", ",", " ", "world", "!"
}

// Line break opportunities
foreach (UnicodeLineBreak lineBreak in Unicode.EnumerateLineBreaks("A long line\nof text"))
{
    // lineBreak.Index: the index of the first character of the next line
    // lineBreak.IsMandatory: after a line feed, and at the end of the text
}

EnumerateLineBreaks takes two optional tailorings, which match the CSS properties line-break and word-break:

  • UnicodeLineBreakStrictness: Strict (the default rules), Normal (a line can start with a small kana), Anywhere (a line can break between any two grapheme clusters).
  • UnicodeWordBreakBehavior: Normal, BreakAll (a line can break between any two letters), KeepAll (no break between letters or ideographs).

No dictionary is used: Thai, Lao, Khmer and the other scripts written without spaces only break at spaces and at zero width spaces. A soft hyphen (U+00AD) is a break opportunity like any other; the caller recognizes it by the character before the break.

Unicode.EnumerateScriptRuns splits a text into runs of one script, for shaping and font selection (UAX #24): combining marks follow their base, spaces and punctuation follow the text before them, Script_Extensions are honored, and both brackets of a pair are in the same run script.

foreach (UnicodeScriptRun run in Unicode.EnumerateScriptRuns("gamma (γ) is"))
{
    // "gamma (" Latin, "γ" Greek, ") is" Latin
}

Hyphenation

The Unicode Standard says where a line can break, including after a soft hyphen (U+00AD), but hyphenating a word needs data for its language. Hyphenator applies the pattern algorithm of TeX (Franklin Liang) to patterns that you provide. No pattern is included in this package: the hyph-utf8 project publishes patterns for many languages, each under its own license.

// The patterns, as plain text: "hy3ph he2n ..." or a \patterns{...} group; exceptions such as "as-so-ciate"
var hyphenator = Hyphenator.Load("hyph-en-us.pat.txt", "hyph-en-us.hyp.txt");

Span<int> positions = stackalloc int[32];
var count = hyphenator.Hyphenate("hyphenation", positions); // 2 positions: 2 and 6 (hy-phen-ation)
  • Hyphenate takes one word, in any case, and returns the indexes before which a hyphen can be inserted. It does not allocate. Use Unicode.EnumerateWordBoundaries to find the words of a text.
  • A break is never inside a grapheme cluster, and leaves at least LeftHyphenMin characters before it and RightHyphenMin after it (2 and 3 by default; the right values depend on the language).
  • The patterns match code points: pass the word in the normalization form of the patterns, usually NFC.
  • Only the plain text form is read. A file that uses TeX macros, \lccode or the ^^ notation is rejected with a FormatException, as is a file that exceeds a limit of HyphenatorOptions (pattern count, pattern length, input length), so that a pattern file from an untrusted source cannot exhaust memory.
  • Hyphenation that changes the spelling of a word is not supported.

Normalization

UnicodeNormalization implements the four normalization forms of UAX #15 without the globalization library of the platform: the results are the same on every platform and in globalization-invariant mode, where string.Normalize only handles ASCII.

UnicodeNormalization.Normalize("cafe\u0301");                            // "café" (NFC)
UnicodeNormalization.Normalize("café", NormalizationForm.FormD);         // "cafe\u0301"
UnicodeNormalization.IsNormalized("café");                               // true
UnicodeNormalization.TryNormalize(source, destination, out var written); // span-based
UnicodeNormalization.TryComposeCanonical('e', 0x0301, out var composite); // true, 'é'

Normalize returns the same instance when the text is already normalized.

Bidirectional text

UnicodeBidi implements the Unicode Bidirectional Algorithm (UAX #9), in the two steps the annex defines: levels per paragraph, then display order per line.

var paragraph = "car \u05D0\u05D1\u05D2!";
Span<byte> levels = stackalloc byte[paragraph.Length];
var paragraphLevel = UnicodeBidi.ResolveLevels(paragraph, UnicodeBidiDirection.Auto, levels);

// For each line, once the paragraph is broken into lines:
UnicodeBidi.ResetLineLevels(paragraph, paragraphLevel, levels);
Span<int> visualToLogical = stackalloc int[paragraph.Length];
UnicodeBidi.GetVisualOrder(levels, visualToLogical); // 0 1 2 3 6 5 4 7
  • A character whose level is odd is displayed right to left.
  • UnicodeBidi.MayRequireBidi is a fast check: when it returns false, every level is 0 and the display order is the order of the text.
  • ResolveLevels has an overload that takes its workspace from the caller (GetWorkspaceLength) and does not allocate.
  • Mirroring (rule L4) is UnicodeProperties.TryGetBidiMirroringGlyph.

Character information

Unicode.GetCharacterInfo gives the full record of a character, including its name. It is meant for inspection tools: prefer UnicodeProperties to read a property of many code points.

var info = Unicode.GetCharacterInfo(new Rune('A'));
if (info is not null)
{
	Console.WriteLine(info.Value.Name); // "LATIN CAPITAL LETTER A"
	Console.WriteLine(info.Value.Category); // UppercaseLetter
	Console.WriteLine(info.Value.BidiCategory); // LeftToRight
	Console.WriteLine(info.Value.Block); // BasicLatin
}
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed.  net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net11.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Meziantou.Framework.Unicode:

Package Downloads
Meziantou.Framework.Imaging

Fully managed, performance-oriented image library for .NET: PNG, APNG, GIF, JPEG, WebP, QOI, BMP, TGA, Netpbm, TIFF, ICO/CUR, ANI, AVIF, HEIC and JPEG 2000 decoding/encoding, JPEG XL decoding and lossless/lossy encoding, lossless JPEG recompression as JPEG XL, animation-aware processing, vector drawing, ICC color conversion, and bounded-memory streaming.

Meziantou.Framework.Fonts

Fully managed OpenType font reader and text shaper shared by Meziantou.Framework.Imaging and Meziantou.Framework.WebPlatform: TrueType, CFF and CFF2 outlines, WOFF and WOFF2, variable fonts, color fonts, and the shaping of complex scripts. Its API is internal to these packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.11 49 10/20/2026
2.0.10 45 10/20/2026
2.0.9 133 10/18/2026
2.0.8 311 10/14/2026
2.0.7 458 10/12/2026
2.0.6 322 9/27/2026
2.0.5 116 9/20/2026
2.0.4 138 9/8/2026
2.0.3 134 9/6/2026
2.0.2 260 8/29/2026
2.0.1 387 7/8/2026
2.0.0 143 7/5/2026
1.0.14 183 6/13/2026
1.0.13 150 6/7/2026
1.0.12 134 5/31/2026
1.0.11 152 5/17/2026
1.0.9 215 4/25/2026
1.0.8 239 2/15/2026
1.0.7 163 1/18/2026
1.0.5 155 1/16/2026
Loading failed