Letras Diferentes
Unicode research · Styled text normalization

Normalizing Styled Unicode Text: Preservation, NFKC Folding and Semantic Loss

How NFC, NFD, NFKC and NFKD affect mathematical styled letters, enclosed symbols, superscripts, combining decorations and copy-paste fidelity across applications.

Technical referenceUnicode baseline: 18.0.0

Styled text is not one uniform Unicode category

What users call “fancy fonts” may be ordinary characters with CSS styling, encoded mathematical alphanumeric symbols, enclosed letters, fullwidth forms, superscripts, subscripts, or strings containing additional combining marks. The first case changes presentation without changing the text. The others can change the actual Unicode scalar sequence. That distinction determines whether normalization preserves or alters an apparently styled label.

For instance, A U+0041 and 𝐀 U+1D400 are two different character identities; the latter belongs to Unicode mathematical alphanumeric symbols and is not simply a bold font selection. Applying font-weight: bold to U+0041 produces no alternate code point, so there is no encoded mathematical style for normalization to fold. Unicode semantics—not a screenshot—determine what normalization does.

NFC/NFD preserve most compatibility styling, but NFKC/NFKD may not

Unicode defines canonical forms NFC and NFD and compatibility forms NFKC and NFKD (UAX #15). NFC/NFD preserve compatibility-only distinctions. NFKC/NFKD recursively apply compatibility decompositions, potentially discarding the original mathematical alphabet, circled enclosure, width, ligature or raised position. A compatibility-folded result is not guaranteed to be the original underlying author's text and normally cannot be inverted.

OriginalNFCNFKCWhat changed?
𝐀 U+1D400𝐀AMathematical bold lost
𝒜 U+1D49C𝒜AMathematical script lost
ℬ U+212CℬBHistoric script BMP symbol folded
A U+FF21AAWidth lost
ⓐ U+24D0ⓐaEnclosing circle lost
² U+00B2²2Raised position lost
x̲ U+0078 U+0332x̲x̲Combining underline remains in this example

A critical exception to slogans like “NFKC removes all decoration” is that normalization does not strip every combining mark, invisible character, emoji sequence or variation selector. Some nonspacing marks remain after NFKC. The Unicode Character Database defines exact mapping behavior; a generic plain-text conversion requires a separately specified policy.

Mathematical alphabets may carry distinct meaning

Mathematical bold A is not just a cosmetic variant of Latin A in formal scientific notation. A bold vector, Fraktur algebra or double-struck number set may need to remain distinct even in plain-text interchange. Mathematical Alphanumeric Symbols contain deliberate unassigned holes for earlier BMP letterlike characters; blindly constructing code points via alphabet offsets is not a substitute for official mappings.

In such contexts, NFKC can collapse multiple mathematically distinct variables to the same Latin character, changing the meaning of a formula. Structured MathML and font-specific math rendering are separate considerations. Never use a blanket compatibility normalization pass on equations without a carefully defined mathematical content model.

Exact code points in JavaScript

const list = ["𝐀","𝒜","ℬ","A","ⓐ","²","x\u0332"];
const points = text => [...text].map(c =>
  "U+"+c.codePointAt(0).toString(16).toUpperCase());
for (const s of list) {
  console.log({before: points(s),nfc:points(s.normalize("NFC")),
    nfkc:points(s.normalize("NFKC"))});
}
console.log("𝐀".normalize("NFC")==="𝐀");   // true
console.log("𝐀".normalize("NFKC")==="A");    // true
console.log("x\u0332".normalize("NFKC")==="x\u0332"); // true

JavaScript iteration using [...text] advances by code point, which is essential for supplementary mathematical characters spanning two UTF-16 code units. Even code-point iteration is not grapheme segmentation; an underline sequence can be one perceived grapheme with two scalar values.

Round-trip preservation and the irreversible map

Original source strings should be stored separately from any search-normalized representation when fidelity is part of the product promise. If an editor folds U+1D400 to U+0041 at save time, a later converter cannot determine whether the user typed ordinary A or mathematical bold A. This is true even when the displayed character looks plausible in the saved document.

For copy-paste tests, collect the source scalar stream, clipboard text/plain stream and destination scalar stream separately. A receiving platform may preserve data but use a fallback font, or it may actually rewrite the stream. Distinguish these two outcomes before diagnosing a compatibility failure. The US Fancy Text tool is a concrete source of copyable styled letters for controlled inspection—not a promise that every destination retains them.

Python normalization and decomposition diagnostics

import unicodedata as ud
for ch in ["𝐀","ℬ","A","ⓐ","²"]:
    print(ud.name(ch), ud.decomposition(ch),
          ud.normalize("NFKC",ch))
assert ud.normalize("NFKC","ℬ") == "B"
assert ud.normalize("NFC","ℬ") == "ℬ"
assert ud.normalize("NFKC","𝐀") == "A"
assert ud.normalize("NFKC","x\u0332") == "x\u0332"

Decomposition strings in UnicodeData use hexadecimal scalar values and may include compatibility tags. They do not mean a browser has selected a different font. Script-specific, presentation-specific and accessible alternatives require independent checks, especially for names, identifiers and scientific text.

Security and accessibility consequences

A visually similar account name may use mathematical styled letters or cross-script confusables. NFKC reduces some compatibility distinctions but does not solve all spoofing or identifier safety issues: UTS #39 and the application's identifier profile must address allowed scripts, context and confusables. For plain-language prose, an all-styled character string can also impose screen-reader or copy-paste burdens compared with semantic formatting.

Use semantic HTML headings and CSS for ordinary emphasis; preserve legitimate encoded symbols when their character identity matters. For case-insensitive or accent-insensitive search, document the full comparison pipeline; normalization, case folding and accent removal are distinct transformations that do not commute in every arbitrary composition.

A reproducible validation matrix

FixtureExpected transformationFailure detected
U+1D400 (𝐀)NFKC → U+0041Loss of mathematical style
U+212C (ℬ)NFKC → U+0042Historical BMP exception
U+FF21 (A)NFC unchanged; NFKC → U+0041Width distinction
U+0078 U+0332Underline remains under NFKCInvalid “strips all marks” assumption
UTF-8 export and reimportExact scalar sequence, absent explicit lossy transformsEncoding mismatch versus normalization
Same glyph after font fallbackInspect code points, not screenshotRendering mistaken for data identity

A conforming normalization library should be verified using the matching version of the Unicode NormalizationTest.txt data file, including boundary cases beyond these illustrative vectors. Products requiring faithful copyable text should treat NFKC as a deliberate, potentially destructive transform.

Standards and implementation references

  1. UAX #15 — Unicode Normalization Forms
  2. UAX #44 — UnicodeData compatibility mappings
  3. Unicode Standard Chapter 22 — Mathematical Alphanumerics
  4. UTS #39 — Unicode Security Mechanisms
  5. W3C MathML Core
  6. MDN — normalize()

Independent technical reference. Normalization and casing rules are algorithmic data transformations, not substitutes for visual or language-specific validation.