utf16_safe_text 1.0.0 copy "utf16_safe_text: ^1.0.0" to clipboard
utf16_safe_text: ^1.0.0 copied to clipboard

Truncate and sanitize Dart strings without splitting UTF-16 surrogate pairs. Prevents the not-well-formed-UTF-16 crash in Flutter text rendering.

utf16_safe_text #

Truncate and sanitize Dart strings without splitting UTF-16 surrogate pairs.

CI

The crash this prevents #

A Dart String is a sequence of UTF-16 code units. Emoji and other characters outside the Basic Multilingual Plane are stored as a surrogate pair β€” two code units that only mean something together. substring counts units, not characters, so it can cut between the halves:

// 'Cem πŸ‘‹' is 6 code units β€” the emoji is the last two.
'Cem πŸ‘‹'.substring(0, 5); // 'Cem ' + half an emoji

Dart carries that string around without complaint. Skia does not. As soon as it reaches the text renderer:

Invalid argument(s): string is not well-formed UTF-16
(dart:ui _NativeParagraphBuilder.addText)

One user with an emoji in their display name is enough to crash every screen that truncates that name.

Usage #

import 'package:utf16_safe_text/utf16_safe_text.dart';

// Length-limit a string. Never splits a pair β€” the emoji is dropped whole.
safeTruncate('Cem πŸ‘‹', 5); // 'Cem '
safeTruncate('Cem πŸ‘‹', 6); // 'Cem πŸ‘‹'

// Repair a string that is already malformed (database, API, another client).
stripLoneSurrogates('Cem \uD83D'); // 'Cem '

// Check before rendering.
isWellFormedUtf16('Cem πŸ‘‹'); // true

Also available as extensions on String:

'Cem πŸ‘‹'.truncateUtf16Safe(5);   // 'Cem '
'Cem \uD83D'.withoutLoneSurrogates; // 'Cem '
'Cem πŸ‘‹'.isWellFormedUtf16;      // true

API #

Function Purpose
safeTruncate(s, maxUnits) Truncates to at most maxUnits code units, stepping back one unit rather than splitting a pair.
stripLoneSurrogates(s) Removes unpaired surrogates, keeping valid pairs. Returns s unchanged (no allocation) when it holds no surrogates.
isWellFormedUtf16(s) true when every surrogate is part of a complete pair β€” i.e. safe to render.

Scope: pairs, not grapheme clusters #

This package guarantees well-formed UTF-16. It does not guarantee that user-perceived characters stay whole.

A family emoji like πŸ‘¨β€πŸ‘©β€πŸ‘§ is several people joined by zero-width joiners. safeTruncate will not produce a malformed string from it β€” nothing crashes β€” but a cut can still leave you with fewer people than you started with. Likewise a combining accent can be separated from its base letter.

That is a deliberate boundary. Guarding against a renderer crash is cheap and belongs everywhere; iterating grapheme clusters is more expensive and is already solved by the characters package:

import 'package:characters/characters.dart';

'πŸ‘¨β€πŸ‘©β€πŸ‘§ hi'.characters.take(1).toString(); // 'πŸ‘¨β€πŸ‘©β€πŸ‘§'

Use characters when you want "3 characters" to mean what a user thinks it means. Use this package when you need a hard code-unit ceiling β€” a database field limit, a protocol constraint β€” without risking malformed output.

Origin #

Extracted from ikiliya, a multiplayer word game where display names come from users and are truncated on nearly every screen.

License #

MIT

0
likes
160
points
5
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Truncate and sanitize Dart strings without splitting UTF-16 surrogate pairs. Prevents the not-well-formed-UTF-16 crash in Flutter text rendering.

Repository (GitHub)
View/report issues

Topics

#unicode #utf16 #string #emoji #text

License

MIT (license)

More

Packages that depend on utf16_safe_text