uwidth is a small C library for measuring Unicode text one grapheme cluster
at a time.
Pass decoded Unicode scalar values to it as a stream. The library reports each completed cluster with a width of 0, 1, or 2. Controls are reported separately so the caller can choose how to handle them. The library does not decode UTF-8 or any other text encoding.
The generated tables use official Unicode 17.0.0 data. They cover combining characters, canonical decomposition, CJK width, emoji, flags, keycaps, and text presentation.
Copy uwidth.h and uwidth.c into your project. The runtime does not allocate
memory and has no dependencies. It uses C99.
Choose a width profile when you initialize the state:
uwidth_profile_narrowgives East Asian Ambiguous characters width 1uwidth_profile_east_asiangives them width 2
Each input must be a Unicode scalar value, which means any code point other than a surrogate. Noncharacters are valid inputs.
uwidth_push consumes one scalar. It returns uwidth_event_none while a
cluster is pending, uwidth_event_cluster with a width, or
uwidth_event_control with a control scalar. A new scalar can complete the
previous cluster. Call uwidth_finish after the last scalar.
This example ignores controls:
#include "uwidth.h"
static unsigned long measure(
const Uwidth_Code_Point *text,
unsigned int length
)
{
Uwidth_State state;
Uwidth_Event event;
Uwidth_Event_Kind kind;
unsigned int index;
unsigned long width;
width = 0UL;
uwidth_init(&state, uwidth_profile_narrow);
for (index = 0U; index < length; ++index) {
kind = uwidth_push(&state, text[index], &event);
if (kind == uwidth_event_cluster) {
width += event.width;
}
}
if (uwidth_finish(&state, &event) == uwidth_event_cluster) {
width += event.width;
}
return width;
}General Category Cc characters produce control events. CRLF is one control
cluster whose reported scalar is CR. uwidth_reset discards pending input and
keeps the selected profile. Copy the complete state when you need two
independent continuations and do not inspect its words.
The UWIDTH_VERSION_* macros report the library version. The
UWIDTH_UNICODE_VERSION_* macros report the Unicode data version.
The library follows UAX #29 for grapheme boundaries and UAX #15 for canonical decomposition. Ordinary width comes from East Asian Width. Emoji and text presentation change width only when the input matches an exact official sequence.
The result is deterministic. A particular font or terminal can still display the same text differently.
The generator requires Python 3.9 or later.
python3 generate.py
python3 generate.py --checkUnicode files are downloaded from unicode.org and cached in
.unicode-data/17.0.0. Their SHA-256 hashes are pinned in the generator.
--check verifies the derived model, encoded tables, official conformance
records, and generated source. Use --data-dir PATH for another cache or
--fetch-only to download the files without generating source.
Build the compact test with:
cc -std=c99 -pedantic -Wall -Wextra -Werror -O2 \
uwidth.c uwidth_test.c -o uwidth_test
./uwidth_testThe model follows UAX #11, UAX #15, UAX #29, and UTS #51.
- Generated C code and the public header: 0BSD
- Generator and runtime template: Apache-2.0 OR MIT
- Unicode data: Unicode License