Changelog

All notable changes to UniText.

Versions
to
2.2.5
2026-05-15
  • Added
    Per-occurrence sprite color: <sprite=name,i> tints the inline sprite with the host UniText component's color (CSS currentColor equivalent); <sprite=name,#FF0000> (or any hex / named color) overrides per occurrence. Omitting the second argument keeps the sprite's original colors as before.
  • Added
    Sprite color inspector: the SpriteModifier parameter editor exposes an Original / Inherit / Override dropdown — choosing Override reveals a color picker — that serializes directly into the tag.
  • Added
    InlineMediaModifier.OnExtraTokens hook: custom inline-media modifier subclasses can parse additional comma-separated arguments after the entry name (per-occurrence tag attributes) without touching the base class.
  • Added
    variant: [ParameterField] type: declare an enum of options where each option is either a literal token or a typed sub-field (color, float, int, bool), so a single tag slot shows a dropdown plus a conditional value editor in the parameter inspector.
  • Changed
    InlineMediaModifier<TEntry, TWrapper>.ConfigureWrapper now takes the cluster index as a third parameter (breaking for custom inline-media modifier subclasses): use it to apply per-occurrence overrides parsed in OnExtraTokens.
  • Fixed
    ShadowModifier.FixedPixelSize offset uneven per glyph: with FixedPixelSize on, each glyph's shadow shifted by a different distance instead of all glyphs sharing one on-screen offset.
2.2.4
2026-05-13
  • Added
    Add Component inherits project default prefab settings: adding UniText or UniTextWorld via Add Component (or Reset in the inspector) now seeds its serialized fields from the prefab assigned in Project Settings → UniText (Text Prefab / World Text Prefab).
2.2.3
2026-05-13
  • Changed
    Compact font-size inspector row: Font Size and Auto Size now share a single row, and when Auto Size is on, Min, Max, and Current Size share one row instead of three.
  • Changed
    Layout "Advanced" foldout: Base Direction, Over Edge, Under Edge, and Leading Distribution moved into a collapsible Advanced foldout inside the Layout section, leaving wrap and alignment as the only fields visible by default.
  • Fixed
    Tooltips missing on colored toggle buttons: the Auto Size, Word Wrap, alignment, preset, and Expand / Highlight / Rendered toggles in the inspector showed no tooltip on hover even when the underlying serialized field declared one.
2.2.2
2026-05-12
  • Fixed
    Tag attribute values lost < and > literals: a quoted attribute value containing < or > (e.g. Unity Input System binding paths like <sprite="<Keyboard>/a">) was truncated at the first inner >; values wrapped in "…" or '…' now preserve any <> characters verbatim with no entity escaping.
2.2.1
2026-05-12
  • Fixed
    Edits to a StylePreset skipped components that had it empty at load: when components were initialized with the preset still empty, later additions to the preset (inspector or AddStyle) had no effect on those components until they were disabled and re-enabled.
2.2.0
2026-05-12
  • Added
    SpriteModifier (default tag name sprite): embed Sprite assets inline with text without authoring a prefab per icon. The catalog of named sprites is supplied by an ISpriteProvider — built-in providers are InlineSpriteProvider (default — list edited on the modifier) and AssetSpriteProvider (shared UniTextSprites asset). Custom providers (input-prompt icons, localisation, item icons) implement ISpriteProvider directly and raise Changed when their resolution result changes.
  • Added
    Shared inline-object catalog (UniTextObjects asset, Assets → Create → UniText → Objects): named inline-prefab catalog for <obj=name> tags shared across components — alternative to editing the inline list per modifier. Mirrors the existing UniTextGradients and the new UniTextSprites (Assets → Create → UniText → Sprites) patterns.
  • Added
    Project-wide style preset (UniTextSettings.GlobalStylePreset): a StylePreset configured once in Project Settings → UniText applies to every component automatically. A new per-component UseGlobalStylePreset toggle (default on) opts a single component out — useful for debug overlays or technical text where global markup rules would interfere. Local Styles and local StylePresets register first and keep override priority on parse-rule conflicts.
  • Added
    UniTextBase.BeforeProcess static event: fires before any text processing in the canvas-update cycle, alongside the existing MeshApplied and AfterProcess.
  • Added
    InlineTagRule: tag rule with HTML void-element semantics — every <tag> / <tag=value> is treated as a single self-closing insertion, the /> shorthand is no longer required, and stray closing tags are silently stripped. Used by the new Inline Sprite and Inline Object presets.
  • Added
    StylePreset runtime mutation API: AddStyle, RemoveStyle, RemoveStyleAt, ClearStyles, plus an always-on Changed event (was editor-only). Components subscribed to the preset rebuild on the next frame.
  • Added
    Custom modifiers automatically appear in the inspector preset picker: any concrete BaseModifier subclass without a curated entry is listed alphabetically under a new Custom group, paired with a full-text RangeRule for immediate use.
  • Added
    Per-modifier accent colours in the inspector: each modifier type gets a stable colour used for its icon tint, the colored label in style entry headers, and the preset toggle button background.
  • Added
    Unified rounded toggle-button style in the inspector with hover outline — alignment buttons, preset toggles, Auto Size, Word Wrap, Use Global Style Preset, and the Expand / Highlight / Rendered toggles now share one visual; the Rendered toggle's accent colour reflects the text-override state (green for Resolver, orange for SetText).
  • Changed
    UniTextSettings.Changed is now Action<string> (breaking): the payload is the property name that changed (nameof(Gradients), nameof(GlobalStylePreset), nameof(Language), nameof(Dictionaries)) or the new UniTextSettings.All sentinel when the whole asset is replaced via SetInstance. Filter with the new UniTextSettings.Affects(changed, interested) helper; existing handlers must add a string parameter.
  • Changed
    IGradientProvider now derives from INamedCatalog<UniTextGradients.NamedGradient> (breaking for custom providers): replace TryGetGradient(string, out Gradient) with TryGet(string, out UniTextGradients.NamedGradient) — the entry exposes the gradient field. Callers that read from a UniTextGradients asset directly are unaffected.
  • Changed
    StylePreset.styles public field removed (breaking): use the new Styles read-only list and the AddStyle / RemoveStyle / ClearStyles mutation API.
  • Changed
    Modifier configuration fields converted to properties (breaking): OutlineModifier.fixedPixelSize, ShadowModifier.fixedPixelSize, ExtrudeModifier.steps / bevel / fixedPixelSize, ListModifier.markerPlacement / bulletMarkers / orderedStyles, CompositeModifier.modifiers, and MaterialModifier's render order / sort index / emoji material / quad padding fields are no longer public — assign through the matching PascalCase property (FixedPixelSize, Steps, MarkerPlacement, RenderOrder, etc.) so the setter requeues the right rebuild stage. Serialized values migrate automatically; only direct field access from runtime code needs updating.
  • Changed
    Nested list items indent under their own parent marker, not under the widest sibling: when a list mixes marker widths between siblings (e.g. an ordered sublist nested under one bullet but not another), the nested column shifts to match its parent's marker width instead of the maximum across the entire list. Matches Google Docs / MS Word; Outside placement and level-0 unchanged.
  • Changed
    Inline Object and Inline Sprite presets use InlineTagRule<obj=name> and <sprite=name> no longer need the /> suffix to self-close.
  • Changed
    GlobalSettingsGradientProvider rebuilds only when the project's gradient asset reference or its entries change; unrelated UniTextSettings edits no longer trigger a gradient rebuild.
  • Removed
    GradientNotifier static class (AnyChanged event, NotifyChanged method): replaced by per-instance INamedCatalog<TEntry>.Changed events on each provider. Custom gradient providers should raise their own Changed when their resolution result changes.
  • Fixed
    GradientModifier.Provider setter ignored the catalog Changed subscription: assigning a new provider at runtime left edits to the new provider unnoticed and kept the old provider driving rebuilds.
2.1.10
2026-05-08
  • Added
    WebGL builds now match the active Emscripten toolchain: the package ships native WebGL binaries built for legacy Emscripten 3.1 (Unity 2021–6000.4) and modern Emscripten 4.0 (Unity 6000.5+), each in default and WebAssembly 2023 long-jump variants, and the editor picks the right one automatically based on Unity version and the WebGL wasm2023 PlayerSetting.
  • Added
    ListMarkerPlacement enum and ListModifier.markerPlacement field: pick between Inside (default — marker takes inline space at the line start, content shifts past it; matches Google Docs, MS Word, Discord) and Outside (marker right-aligned to a fixed content column, hangs into the leading margin if wider; equivalent to CSS list-style-position: outside), direction-aware in both LTR and RTL paragraphs.
  • Changed
    ListModifier.indentPerLevel field removed (breaking — serialized values are dropped by Unity): the per-nesting-level indent step is now derived from the widest marker in the list instead of a fixed 0.55em × fontSize, so columns auto-fit any marker style without manual tuning. Visible difference is at nested levels — wider for lists with multi-digit numbers or long bullets, narrower for short ones; level-0 layout is unchanged.
  • Changed
    <indent> now indents from the tag opening, not just from the next line: when the tag opens mid-line, content following the open boundary visibly shifts within the current line; previously only wrapped lines whose first codepoint sat inside the tag were pushed.
  • Fixed
    Markdown list items folded a non-indented following paragraph: a plain-text line at the same or lower indent than a -/*/+/numbered item was absorbed into the previous list item instead of breaking out into its own paragraph.
  • Fixed
    CreateAssetWithContent obsolete-API warning on Unity 6000.4+: creating a custom UniText shader from Assets > Create > UniText > Custom Shader raised a deprecation warning on Unity 6000.4+.
2.1.9
2026-05-07
  • Added
    <br> hard line break (LineBreakParseRule, standalone — register without a modifier): forces a line break wherever <br>, <br/>, or <br /> appears in the source, with HTML void-element semantics (no closing tag, no nested content); the new line keeps the surrounding paragraph's direction, alignment, and styling.
  • Added
    IndentModifier (default tag name indent): adds left indent to every line whose first codepoint falls inside the tagged range, accepting px, em, % (of the host RectTransform width, matching CSS text-indent semantics), and signed deltas; nested or overlapping tags compose additively like CSS margin-left.
2.1.8
2026-05-06
  • Added
    UniTextUnpackEffectColor (in UniText_Custom.cginc): decodes a Color32 packed by EffectPacking.PackColor from a UV channel with the correct sRGB-to-linear conversion for the active color space — custom material shaders that read effect colors should call it instead of unpacking by hand.
  • Fixed
    Effect colors looked washed out in Linear color space: outline, shadow, extrude, and other effect-modifier colors rendered noticeably brighter than the face text in projects using Linear rendering.
2.1.7
2026-05-02
  • Fixed
    Family emoji and other ZWJ sequences misplaced in RTL paragraphs: parts of multi-glyph emoji (e.g. the children in 👨‍👩‍👧‍👦) drifted away from the main composition when the surrounding text was Arabic or Hebrew, while the same emoji rendered correctly in LTR text.
2.1.6
2026-04-30
  • Fixed
    Text inside RectMask2D clipped off too early: a UniText whose glyphs extended past its RectTransform (long words without word wrap, outline / glow, modifier offsets) disappeared as soon as the rect itself left the mask, even though the rendered text was still inside the clip area.
2.1.5
2026-04-29
  • Added
    UniTextWorld.RaycastTarget (default true, inspector + property): turn off on purely decorative world-space text and the camera's UniTextWorldRaycaster skips it entirely, mirroring Canvas Graphic.raycastTarget.
  • Added
    One-time warning when an interactive UniTextWorld plays in a scene without a UniTextWorldRaycaster: instead of pointer events silently doing nothing, a single Console warning points at the camera that needs the raycaster (or at RaycastTarget = false for decorative text).
  • Added
    UniTextBase.CollectRangeEntries(int, int, PooledList<LineRangeEntry>) + public LineRangeEntry struct: one entry per contiguous glyph run within a line for a cluster range, with X clamped to the line's visible content extent — usable by custom modifiers and tools that need glyph-accurate spans, not just bounding rects.
  • Added
    TextLine.glyphStart / glyphCount / widthPx / IsRtl (public): the positioned-glyph range and mesh-local content width are now exposed on each line for custom modifiers reading layout output, along with a paragraph-direction flag from the BiDi level.
  • Added
    CanvasHighlightRenderer and WorldHighlightRenderer are now subclassable (public): plug a custom Canvas-side or world-space highlight visual without reimplementing the lifecycle.
  • Added
    Type-safe per-backend TextHighlighter extension points: subclasses now override CreateHighlightRenderer(UniText, ...) and / or CreateHighlightRenderer(UniTextWorld, ...) to plug a custom visual on the chosen backend; subclassing DefaultTextHighlighter keeps its click / hover / selection logic and only swaps the visual.
  • Changed
    UniTextBase.CreateHighlightRenderer(string, HighlightOrder) removed (breaking for custom highlighter authors): the abstract owner-side hook is gone — implement the typed TextHighlighter.CreateHighlightRenderer(UniText, ...) / CreateHighlightRenderer(UniTextWorld, ...) overloads instead, and call the protected untyped CreateHighlightRenderer(name, order) from event handlers.
  • Changed
    TextHighlighter.OnSelectionChanged removed (breaking for custom highlighter authors): drive selection visuals from your own state — DefaultTextHighlighter.SetSelection shows the intended pattern.
  • Changed
    GameObject > UI (World) > UniText > World Text no longer auto-adds UniTextWorldRaycaster to Camera.main: pick the camera explicitly; the new in-scene warning will tell you when it's missing.
  • Changed
    Double-line decoration: <u double> / <s double> now renders each sub-line at the full requested thickness with a same-thickness gap (was: 35% / 30% / 35% summing to the requested thickness), making double underlines and strikethroughs noticeably bolder.
  • Changed
    Underline / strikethrough end-caps scale with the line's thickness instead of the underscore-glyph height: thinner lines get proportionally narrower caps for cleaner edges; thick lines keep close to the previous look.
  • Fixed
    AutoSize text shrunk and didn't grow back when the rect grew taller: after the shrink-to-fit pass reduced the effective font size to fit the height, increasing the rect's height (without changing the width) left the text shrunken until the width changed.
  • Fixed
    Thick underlines / strikethroughs clipped at the top and bottom edges: when the requested line thickness exceeded the underscore glyph's natural rendered height, SDF sampling fell off the glyph and lost ink near the top / bottom of the line; the sampled region now grows with the requested thickness.
  • Fixed
    Dotted and dashed underlines / strikethroughs drew a partial last mark past the line end: the pattern loop now stops at the last mark that fits entirely within the segment.
2.1.4
2026-04-29
  • Added
    Underline / strikethrough styles: UnderlineModifier and StrikethroughModifier accept a 5-field parameter — thickness (em or px), offset (em or px), style (solid, double, dotted, dashed), skip-ink (line breaks around descenders like g, j, p, q, y), and overlay (line draws above the text instead of behind it); bare <u>/<s> still defaults to a solid line at the font's metrics.
  • Added
    Scene Visibility opt-out: a Scene view overlay and Tools > UniText > Respect Scene Visibility menu toggle whether hiding a UniText / UniTextWorld GameObject in the Hierarchy clears its rendered text (default: on; per-developer, stored in EditorPrefs).
  • Added
    UniTextMeshGenerator.EffectPass + currentEffectPass (for custom effect modifiers): a modifier can place its duplicate quads above (PostFace) or below (PreFace) the face of the current glyph, so e.g. an outline modifier draws around an overlay decoration line on top of the text rather than behind it.
  • Added
    UniTextMeshGenerator.isVirtualGlyph (for custom modifiers): the per-glyph callback now fires for modifier-injected quads (decoration lines, kashida, list markers); read this flag to skip them when only real shaped glyphs matter.
  • Added
    UniTextMeshGenerator.QueueEffectTriangle + RequestBandUpgradeIfNeeded (for custom modifiers that emit their own quads): public helpers to route effect triangles through the shared pre/post-face buffer and to request a wider SDF tile for the current quad without duplicating internal logic.
  • Added
    LineRenderHelper.DrawDot (for custom decoration modifiers): emits one bullet-shaped dot quad sampled from the font's bullet glyph (U+2022), with a stretched-underscore fallback when the font has no bullet.
  • Added
    UniText/Lit/SDF and UniText/Lit/Emoji now render under URP: world-space lit text works in Universal Render Pipeline projects from URP 12 (Unity 2021.3 LTS) through URP 17 (Unity 6), receives main-light shadows and additional-light shadows, and uses Forward+ cluster lighting on URP 14+.
  • Added
    Lit shaders cast shadows: world-space text using UniText/Lit/SDF or UniText/Lit/Emoji now contributes to shadow maps in both Built-in and URP — SDF silhouette is driven by glyph dilate (effect-mode outlines also cast their inflated shape), and emoji uses bitmap alpha with the new _ShadowCutoff material property.
  • Added
    Lit shaders react to nearby point and spot lights: additional non-important point/spot lights now affect world-space lit text in both pipelines (up to 4 vertex-evaluated in Built-in; per-pixel with shadow attenuation in URP).
  • Changed
    UniTextMeshGenerator.Current removed (breaking for custom modifiers): replace with the per-component instance uniText.MeshGenerator.
  • Changed
    UniTextMeshGenerator.onAfterPage split (breaking for custom modifiers) into onMainPassComplete (emit decoration geometry — also runs through the per-glyph pipeline) and onMainPassFinalize (effect modifier flushes); subscribe to whichever your previous handler was for.
  • Changed
    LineRenderHelper.DrawLine signature (breaking for custom decoration-line modifiers): now takes the generator, cluster, UV cap range, and an explicit thickness override; color is no longer a parameter — it flows through the per-glyph color / gradient / effect pipeline.
  • Fixed
    Parameter field reset when switching the inspector between objects sharing the same Style layout: when two assets or components each had a Style at the same index (e.g. a StylePreset and a UniText), switching selection between them reset the newly-selected object's parameter field to the modifier's defaults.
  • Fixed
    <b> / <i> / <var> ignored the family chosen by FontModifier: combining <font=X> with <b>, <i>, or <var> resolved the bold/italic face or variable axis from the fallback family instead of the family named by FontModifier, so e.g. <font=Roboto><b> could render Roboto Regular instead of Roboto Bold.
  • Fixed
    UniTextWorld reported infinite mesh bounds: every world-space text shard reported a 2 km axis-aligned bounding box, breaking frustum culling, shadow caster volumes, and Renderer.bounds queries; bounds are now computed from the actual mesh vertices in each shard.
  • Fixed
    UniTextWorld ignored GameObject Layer: world-space text drew through every camera regardless of culling masks, because the batcher merged all components into one shared layer; the batch now keys on the component's Layer (and re-routes when the Layer changes at runtime), so cameras honor their culling mask.
  • Fixed
    Underline, strikethrough, and Arabic kashida ignored per-glyph effects: gradient, outline, shadow, and custom-material modifiers applied to text but skipped its decoration lines and kashida elongation; decoration geometry now runs through the same per-glyph pipeline and picks up all active modifiers uniformly.
2.1.3
2026-04-27
  • Added
    ExtrudeModifier: adds a 3D extrude / bevel stack behind the text with a per-step color gradient from near to far, configurable offset, dilate, and softness; an optional bevel mode adds intermediate side-faces for chamfered depth. Step count and bevel toggle live on the modifier; tag parameter format: offsetX,offsetY,#nearColor,#farColor,dilate,softness.
  • Added
    EffectModifier per-layer flush hooks (for custom multi-layer effect subclasses): ApplyOwnRequests is now protected virtual and AppendSharedEffectQuad is protected static, so a subclass can buffer its own per-layer requests and flush them in painter order across all glyphs instead of the default per-glyph order.
  • Changed
    EffectPacking.PackColor returns Vector2 (breaking for custom effect modifiers and shaders): packed color now occupies texcoord2.y and texcoord2.z, and custom shaders must call UnpackColor(input.texcoord2.y, input.texcoord2.z) — the single-float UnpackColor(float) overload is gone.
  • Changed
    Color alpha is composited with the component's base alpha: <color=#RRGGBBAA> ranges, gradient stops, and underline / strikethrough colours now multiply their alpha with the component alpha instead of discarding it, so <color=#FF000080> renders at 50% opacity (was: forced to component alpha). Use a fully opaque parameter to restore the previous look.
  • Changed
    LineHeightModifier: single value parameter, no mode: the inspector now shows one Value field, and <lh=N> always sets line height. Existing <lh=h,N> markup still parses unchanged; existing <lh=s,N> parses but now sets height — use <lh=+N> for the additive equivalent.
  • Changed
    Glyph Diagnostic menu moved: now under Tools > UniText > Glyph Diagnostic (was top-level UniText > Glyph Diagnostic).
  • Fixed
    Outline and shadow color randomly tinted on some GPUs: the previous color packing produced NaN/Inf bit patterns that some drivers canonicalize at the vertex–fragment interpolator boundary, randomly altering the green channel as colors crossed certain thresholds.
  • Fixed
    Multiple GradientModifier instances on one component overwrote each other: each instance kept a private 1-based gradient list and stomped the shared per-codepoint index buffer, so the second modifier silently replaced the first one's gradient assignments.
  • Fixed
    Parameter field stuck on stale values after changing the modifier type: switching a Style's Modifier (or a child of CompositeModifier) in the inspector now resets the parameter field to the new modifier's defaults instead of keeping the previous modifier's text.
2.1.2
2026-04-26
  • Added
    UniTextBase.Animated event: raised after Unity Animator applies animated property values to a UniText / UniTextWorld; modifiers with their own animatable fields can subscribe, diff their state, and call SetDirty with the matching UniTextDirtyFlags.
  • Added
    AnimationHandlerBase<T>: public base class for extending the built-in Animator diff with subclass-specific animatable fields when authoring a custom UniTextBase subclass.
  • Fixed
    Unity Animator did not update rendered text: animating fontSize, color, wordWrap, autoSize, minFontSize / maxFontSize, baseDirection, horizontalAlignment / verticalAlignment, overEdge / underEdge, leadingDistribution — and on UniTextWorld also sortingOrder / sortingLayerID — silently had no visual effect.
2.1.1
2026-04-26
  • Added
    IGradientProvider with three built-in implementations — GlobalSettingsGradientProvider (default, reads UniTextSettings.Gradients), AssetGradientProvider (per-modifier asset reference), InlineGradientProvider (inline list edited on the modifier itself); pick the source for each GradientModifier from the inspector.
  • Added
    Live gradient preview in the GradientModifier parameter dropdown: each row shows the actual gradient swatch on the right and reflects the provider currently assigned to that modifier, not just the project-wide settings.
  • Added
    GradientNotifier: static AnyChanged event raised when any gradient source visible to GradientModifier is edited (asset, inline list, or a custom provider invoking NotifyChanged); affected text rebuilds on the next frame without manual refresh.
  • Added
    Public Unicode character properties for modifier / parse-rule authors: UnicodeData.GetSimpleUppercase / GetSimpleLowercase / GetSimpleTitlecase, GetGeneralCategory (+ public GeneralCategory enum), GetScript, IsExtendedPictographic / IsEmojiPresentation / IsEmojiModifierBase, IsDefaultIgnorable — backed by bundled UCD tables and identical across Mono, IL2CPP, and standard .NET.
  • Added
    ParameterOption + ContextualParameterOptionsProvider: extension API for [ParameterField("enum:@key")] dropdowns — options can carry a display label, a per-row preview decorator, and a description, and can be derived from the owning modifier instance.
  • Changed
    UppercaseModifier / LowercaseModifier / SmallCapsModifier resolve case via the bundled Unicode case mapping table instead of char.ToUpper/LowerInvariant; behavior is identical across Mono, IL2CPP, and standard .NET runtimes.
  • Fixed
    Emoji rendered as .notdef inside a FontModifier range: emoji codepoints in a range covered by FontModifier were forced through the chosen text font, which has no emoji glyphs; emoji now always resolve to the emoji font regardless of any explicit font override.
  • Fixed
    FontModifier did not fall back to the FontStack chain: codepoints not covered by the named family produced .notdef instead of falling through the standard fallback chain (as the docs already promised).
  • Fixed
    UppercaseModifier skipped Greek final sigma (U+03C2 ς): the last character of words like "πόνος" was left lowercase due to a runtime gap in Mono's case tables.
2.1.0
2026-04-25
  • Added
    Language-aware shaping (BCP 47 + OpenType locl): fonts with language-specific glyphs (pan-CJK like Noto Sans CJK / Source Han Sans) now render the correct regional forms. Apply per-range with LanguageModifier, per-component via UniText.Language, or project-wide via UniTextSettings.Language.
  • Added
    FontModifier: override the font on a text range by referencing a FontFamily.name from the component's UniTextFontStack. A matched family wins over both preferredLanguage selection and the default fallback chain; the normal chain still kicks in for codepoints the chosen family can't render. Unknown names log a one-time warning.
  • Added
    Per-family language hint: FontFamily.preferredLanguage — one font stack can hold region-specific cuts (SC/TC/JP/KR) and pick the right one automatically from the active language.
  • Added
    Named font families: FontFamily.name lets you address a family directly from FontModifier or code instead of relying on fallback order.
  • Added
    World-space batcher shard size: UniTextSettings.WorldBatcherShardTargetVertexCount to tune batching granularity vs. rebuild cost for dense world-space scenes.
  • Added
    Custom sub-mesh emission: a modifier can now emit its own geometry with a custom material/atlas that renders Under, Above, or alongside the base text, ordered by a sortIndex — via UniTextMeshGenerator.onCollectSubMeshes and UniTextRenderData.
  • Added
    Quad expansion API: UniTextMeshGenerator.ExpandQuad + faceBaseIdx + DefaultSdfPadding — a supported way for effect modifiers to grow a glyph quad so wide outlines / fake-bold / soft shadow don't clip at the quad edge.
  • Added
    Text-model properties on UniTextBase: four zero-alloc views covering the full pipeline from authored text to what's drawn.
  • Added
    Text resolver hook (IUniTextResolver + UniTextBase.TextResolver): override a component's source text (localization preview, template expansion, key-to-string lookup) without writing to the serialized text field, so scenes and prefabs don't get marked dirty.
  • Added
    SetText(ReadOnlyMemory<char>) / SetText(string): assign text at runtime without writing to the serialized field and without marking the scene/prefab dirty.
  • Added
    UniText.Language property: one-line way to apply a BCP 47 language to the whole text from code, without building a style manually.
  • Added
    Click / hover / selection highlighting on UniTextWorld: the Highlighter slot now lives on UniTextBase and works unchanged on both Canvas and world-space text.
  • Added
    Custom highlighter API: TextHighlighter subclasses can now target both Canvas and world-space text by requesting a backend-agnostic surface — owner.CreateHighlightRenderer(name, HighlightOrder.Behind | Above) returns a TextHighlightRenderer with Color, SetRects(...), Clear(), Destroy().
  • Added
    Style/modifier query and mutation API on UniTextBase: HasModifier<T>(), TryGetStyle<T>(), SetWholeText<T>(parameter), ClearWholeText<T>(), ToggleWholeText<T>(parameter), GetWholeTextParameter<T>() and non-generic Type overloads. Replaces the manual new Style { Rule = new RangeRule { data = ... }, Modifier = ... } boilerplate for programmatic styling.
  • Added
    UniTextWorld public events + active registry: static Activated / Deactivated, per-instance RenderDataAvailable / RenderDataCleared / SortingChanged / ParentChanged, and a UniTextWorld.Active list of currently enabled instances. Observe world-space text state without scene scans.
  • Added
    Click / hover on UniTextWorld: add a UniTextWorldRaycaster component to a Camera and world-space text receives the same pointer events that worked on Canvas — RangeClicked / RangeEntered / RangeExited, link and hashtag events. No per-text colliders needed. Optional BlockingObjects setting to respect 2D/3D physical geometry as occluders.
  • Added
    UniText in Add Component menu: discoverable under UI (Canvas) > UniText in the inspector's Add Component dropdown.
  • Added
    MaterialModifier: apply a custom Material to a text range. Shader gets the glyph atlas as a Texture2DArray, two constant per-text UV4 channels (ConstantUv2/ConstantUv3) for runtime-animated shader params, and an optional per-glyph UV writer for staggered effects. Three compose modes — Replace (hide the base text on the range), Over, Under. Parameter is an optional tint color. Separate emojiMaterial slot for emoji glyphs inside the range.
  • Added
    Protection parse rules: three standalone rules that shield content from any other parse rule (no pairing modifier needed) — NoparseTagRule (<noparse>…</noparse>, forgiving close), CodeSpanRule (balanced backtick runs per CommonMark §6.1: ` x , `x , `x` ), and BackslashEscapeRule (\*, \[, …, full CommonMark ASCII punctuation set).
  • Added
    Standalone parse rules: a rule can be registered on UniText without a paired modifier (opt-in via IParseRule.IsStandalone) — it applies its effect on its own. Used by the three protection rules above.
  • Added
    Style static builders: Style.WholeText(modifier, parameter), Style.Range(modifier, start, end, parameter), Style.Tag(modifier, tagName, defaultParameter) — replace the new Style { Modifier = ..., Rule = new RangeRule { data = new() { ... } } } boilerplate when building styles in code.
  • Added
    RangeEx.WholeText / RangeEx.IsWholeText(...): canonical ".." constant and a predicate that accepts any equivalent syntactic form ("..", "..^0", "0..") — useful when building rules from user input.
  • Added
    SubMeshModifier abstract base class: base class for writing your own modifiers that produce a separate sub-mesh with its own material (the same surface MaterialModifier is built on).
  • Added
    Custom shader authoring: Assets/Create > UniText > Custom Material Shader menu scaffolds a new shader pre-wired for MaterialModifier (uses UniText_Custom.cginc, binds the glyph atlas Texture2DArray). Three example shaders ship as starting points (Dissolve, Hologram, Rainbow).
  • Added
    Noise generator: Tools > UniText > Noise Generator window produces seamless grayscale value / FBM PNG textures (64–1024 px, configurable seed / frequency / octaves / lacunarity / gain / invert / tileable). Used by the example shaders; available for any procedural need.
  • Added
    Lit shaders for world-space text: UniText/Lit/SDF and UniText/Lit/Emoji pick up ambient + a single directional light + fog, suitable for UniTextWorld in a 3D scene.
  • Added
    Default materials: ready-to-use UniTextLit, UniTextEmojiLit, UniTextDisolve, UniTextHologram, UniTextRainbow materials in Defaults/Materials/ (drop on a MaterialModifier or assign as the material of a UniTextWorld).
  • Added
    GameObject > UI (World) > UniText > World Text menu item: creates a ready-to-go UniTextWorld object and auto-adds a UniTextWorldRaycaster to Camera.main so pointer events work out of the box.
  • Added
    Basic Usage sample extended: new Language and Font sections, plus a bundled Source Han Sans subset (Fonts/SourceHanSans-Demo.otf, ~96 KB, SIL OFL 1.1) that actually shows CJK regional-glyph differences in the Language example.
  • Added
    Language APIs (public): LanguageRegistry.Register/GetHandle/GetTag, LanguageMatching.Matches, Shaper.ShapeInto(..., IntPtr language) overload, HB.LanguageFromString / HB.SetLanguage / HB.ShapeRun(..., IntPtr language, ...), UniTextFontStack.FindFontForCodepoint(uint, string preferredLanguage, ...), UniTextFontProvider.FindFontForCodepoint(int, byte language) — for code that drives shaping manually.
  • Added
    UniTextFontStack.TryGetFamilyByName(name, out family) / UniTextFontProvider.TryGetFontIdByFamilyName(name): resolve a FontFamily.name to a family or fontId at runtime.
  • Added
    SharedFontCache.TryGet / Set language overloads: per-codepoint font-cache key now includes the active language, so the same codepoint can cache different results under different language tags.
  • Added
    UniTextBuffers.PrepareStartMargins(): for modifier authors writing start-margin values (list indentation etc.) — lazily allocates the buffer to fit the current codepoint count.
  • Added
    PooledBuffer<T>.ClearAll() / PooledArrayAttribute<T>.ClearAll(): clear the entire backing array (not just the [0..count) prefix) — matches the modifier-attribute usage pattern where the buffer is read at arbitrary indices.
  • Added
    UniTextMaterialCache.Highlight: shared flat-colour transparent material used for range highlights (exposed for custom highlighter renderers).
  • Added
    Run.language / ShapedRun.language (public struct fields): carries the language-registry index through the pipeline.
  • Added
    Project-wide language in Project Settings: a Localization section in the UniText Settings panel edits UniTextSettings.Language without writing code.
  • Added
    Tile Size Offset per-font setting (UniTextFont inspector): nudge the auto-classified SDF atlas tile size up or down by ±2 steps to force higher quality or save atlas memory; ignored on glyphs that have an explicit per-glyph tile override.
  • Changed
    Faster first-frame glyph generation: SDF/MSDF preparation scales much better with glyph complexity — the internal contour-overlap check is now linear instead of quadratic in the number of curve segments per glyph. Biggest wins on CJK, decorative, and symbol fonts.
  • Changed
    Inspector modifier/rule picker: the popup now sizes to its content and resizes when groups expand/collapse, instead of truncating at 15 items.
  • Changed
    FontStack inspector (collapsed family row): shows name and primary inline so you can rename / swap fonts without expanding the foldout.
  • Changed
    Dirty flags / render mode enums lifted to top level (breaking): UniTextBase.DirtyFlagsUniTextDirtyFlags, UniTextBase.RenderModeeUniTextRenderMode. Code referencing the old nested enums will not compile.
  • Changed
    CleanText return type (breaking): stringReadOnlySpan<char>. The backing buffer is pooled and may be rewritten on the next rebuild; copy via new string(span) if you need a stable string.
  • Changed
    Text getter semantics (potentially breaking): always returns the serialized authored value, even when a buffer-based SetText has overridden the runtime text. Read the runtime-assigned text via RawText (or RenderedText if a resolver is in play).
  • Changed
    TextHighlighter.Initialize(UniText)Initialize(UniTextBase) (breaking for custom highlighter subclasses). The owner field type switched from UniText to UniTextBase for the same reason — highlighter now works on both Canvas and world-space text.
  • Changed
    Per-tag parse-rule classes are now internal [Obsolete] (breaking if referenced in code): BoldParseRule, ItalicParseRule, ColorParseRule, SizeParseRule, UnderlineParseRule, StrikethroughParseRule, CSpaceParseRule, LineSpacingParseRule, LineHeightParseRule, OutlineParseRule, ShadowParseRule, ObjParseRule, EllipsisTagRule, UppercaseParseRule, GradientParseRule, LinkTagParseRule. Existing serialized assets still deserialize; new code should use TagRule (directly or via Style.Tag(modifier, "name")).
  • Changed
    Custom EffectModifier subclasses (breaking): the extension hook is now OnGlyphEffect() + EnqueueEffectQuad(...) instead of RecordEffectGlyph(...), and the HasVertexShifts() override is gone. Built-in outline/shadow are unchanged for consumers; this only matters if you wrote your own effect subclass.
  • Fixed
    Auto-size on UniTextWorld: autoSize on world-space text silently fell back to maxFontSize — the size-fitting step was Canvas-only. It now runs for world-space components too.
  • Fixed
    UniTextWorld sorting vs. other renderers: world-space text was batched into one mesh regardless of each instance's sorting layer/order, so it rendered in front of or behind SpriteRenderer and other renderers as one block instead of interleaving per-instance. The batcher now groups by (material, SortingLayer, OrderInLayer, SortingGroup), so each group becomes its own draw with the correct sorting.
  • Fixed
    Outline / shadow artifacts on emoji: OutlineModifier and ShadowModifier applied their effect passes to emoji glyphs too, which rendered color bitmaps through an SDF effect shader and produced garbage. Both now skip color bitmap glyphs.
2.0.15
2026-04-20
  • Fixed
    WebGL emoji disappearing after text change: Reusing the same emoji in a later text update left a transparent gap where the emoji should be rendered.
  • Fixed
    WebGL emoji missing when not at the start of text: Emoji appearing after any preceding characters — including dual-presentation symbols like ⬅, ➡, ❤, ☀ — were not rendered and took no layout space.
  • Fixed
    <size> tag spreading letters apart at non-100% scales: Letters inside a size-scaled range kept their original spacing and bearings while only the glyph quads shrank or grew, so small scales (e.g. <size=10%>) produced tiny letters scattered across the original word width instead of a proportionally compact word.
  • Fixed
    Diacritics detached from base letter inside <size>: Arabic and other combining marks floated far from their base glyph when a per-range size scale was applied, because mark offsets were not scaled along with the base advance.
2.0.14
2026-04-18
  • Fixed
    RTL list marker position unstable: Bullet and number markers in right-to-left lists (Arabic, Hebrew) shifted unpredictably when the item text changed and could render in the wrong position depending on the first character of the line.
2.0.13
2026-04-18
  • Changed
    Temporarily disabled native atlas upload path: Atlas texture updates fall back to the standard upload to avoid crashes on macOS and glyph corruption seen in 2.0.0–2.0.12. Native path will return once stabilized.
2.0.12
2026-04-17
  • Fixed
    Korean text breaking mid-word: The line breaker could split Korean (Hangul) text between adjacent syllables at optional break points, producing broken line wraps inside words.
  • Fixed
    SDF/MSDF artifacts inside glyphs with holes: Bridge regions between overlapping contours (letters such as O, A, B, D, e) produced false distance gradients, visible as faint streaks or specks inside the hollow areas of the glyph.
  • Fixed
    enableWordWrap toggle not re-flowing text: Switching the word-wrap setting between updates could reuse the cached line layout from the previous mode, leaving text wrapped (or unwrapped) incorrectly until another layout-invalidating change.
2.0.11
2026-04-15
  • Fixed
    Emoji ignoring RectMask2D soft edges: Emoji glyphs were clipped with a hard edge under a RectMask2D with non-zero softness, while surrounding UI elements faded smoothly across the same boundary.
2.0.10
2026-04-15
  • Fixed
    Style preset effects leaking onto other text: Modifiers inside a StylePreset (bold/italic/underline used via CompositeModifier) kept their attribute flags and event subscriptions between text updates, so italic, underline, and bold visibly appeared on unrelated characters after switching to text that did not use the preset tags.
2.0.9
2026-04-15
  • Fixed
    Underline/Strikethrough skipping lines at small font sizes: At Font Size ≤3.6, every other line rendered without its underline or strikethrough — lines 1 and 3 got a line, lines 2 and 4 were bare.
  • Fixed
    UniText Text/Button created outside prefab in Prefab Mode: Right-clicking empty space in the Hierarchy while editing a prefab placed the new UniText - Text or UniText - Button under a Canvas in the open scene instead of inside the prefab.
2.0.8
2026-04-14
  • Added
    ParameterReader exposed as public API: Custom modifier authors can now parse tag parameters (floats, unit floats, colors, tokens) using the same locale-safe reader as built-in modifiers.
  • Added
    GlyphAtlas read-only introspection API: The SDF and MSDF atlases are now reachable via GlyphAtlas.GetInstance(RenderMode), with TryGetEntry returning a public GlyphEntry (page index, encoded tile, glyph metrics, pixel size). Key-building helpers MakeKey, DefaultVarHash, ComputeVarHash48, the TileSizeFromEncoded decoder, and constants Pad/PageStride/DefaultBandPixels are also accessible for tooling and custom renderers.
  • Changed
    Editor menus reorganized: Tools/UniText Tools and Tools/UniText Migration consolidated under the Tools/UniText/ submenu. GameObject creation moved from GameObject/UI/UniText - Text|Button to GameObject/UI (Canvas)/UniText/Text|Button.
  • Fixed
    Trailing empty line missing from range bounds: GetRangeBounds skipped the empty line produced by a trailing newline (e.g. "abc\n"), so selection highlighting and link/hashtag bounds covering that line returned no rectangle.
  • Fixed
    Stencil material showing wrong atlas texture: Text under a Mask could render with a stale or mismatched atlas texture after an atlas resize, especially when a renderer group mixed text and emoji glyphs.
  • Fixed
    Atlas shrink corrupting glyphs: Trimming unused atlas pages used a mixed GPU/CPU copy path that could leave stale slice data and drop or corrupt glyphs after the atlas compacted.
2.0.7
2026-04-08
  • Fixed
    Font Subsetter dropping OpenType layout tables: Subset fonts lost all GSUB/GPOS/GDEF/kern tables, breaking contextual shaping (Arabic connected forms, Indic conjuncts, ligatures, kerning).
  • Fixed
    Android 16KB page size compatibility: Native GPU library failed to load on Android 15+ devices with 16KB memory pages.
2.0.3
2026-04-07
  • Added
    Runtime Style Preset API: AddStylePreset(), RemoveStylePreset(), and ClearStylePresets() methods for assigning shared style presets to text components through code.
  • Fixed
    Text invisible after reparenting out of a Mask: Moving a UniText object from under a Mask at runtime via SetParent left stale stencil material, causing text to disappear.
  • Fixed
    Editor errors when reverting a prefab with nested UniText: "Revert All" on a prefab containing a nested UniText component produced MissingReferenceException for destroyed CanvasRenderer.
  • Fixed
    Click events not reaching parent Button: UniText nested inside a Button blocked OnPointerClick from reaching the parent. Now pointer events propagate to the parent unless an interactive range (link, hashtag, etc.) is clicked.
  • Fixed
    RTL list marker offset on wrapped lines: List markers shifted closer to text when an RTL line was wrapped by the line breaking algorithm.
2.0.2
2026-04-06
  • Fixed
    iOS emoji sequences not combining: Emoji with skin tone modifiers, ZWJ sequences, and flag sequences rendered as separate glyphs on iOS. Fixed by shaping emoji through CoreText with kCTTypesetterOptionAllowUnboundedLayout.
  • Fixed
    <lh> delta units: Delta values (<lh=+5px>) were treated as absolute instead of adding to the default line advance.
  • Fixed
    <lh> overridden by globalMinAdvance: Custom line heights set by <lh> were silently replaced by the global minimum advance. Now only applies to lines with default height.
2.0.1
2026-04-04
  • Fixed
    GPU texture upload on Vulkan (Windows): Native GPU upload path was disabled for Vulkan, falling back to Texture2D.Apply().
  • Fixed
    GlyphAtlas resize crash: Atlas resize with 1 slice caused Unity to collapse Texture2DArray into Texture2D, breaking native upload.
  • Fixed
    GlyphAtlas resize losing glyphs: Resize used Graphics.CopyTexture which could fail. Now re-uploads dirty slices via native GPU upload.
  • Fixed
    Unity 2021 compatibility: TextureCreationFlags.DontInitializePixels | DontUploadUponCreate guarded behind UNITY_2022_1_OR_NEWER.
  • Fixed
    Scene Visibility not hiding UniText/UniTextWorld: Eye icon toggle in hierarchy had no effect.
  • Fixed
    UniTextWorld invisible in Prefab Stage: World-space text was invisible when editing prefabs. Batcher now creates a separate instance per Prefab Stage
2.0.0
2026-04-01
  • Added
    GlyphAtlas (Runtime/FontCore/GlyphAtlas.cs): Shared Texture2DArray-backed glyph atlas with two singleton instances — one for SDF (RHalf) and one for MSDF (RGBAHalf). Features adaptive tile sizes (64/128/256 based on glyph complexity), shelf-based packing within 2048x2048 pages, reference counting with LRU eviction, automatic page recycling, and atlas shrinking.
  • Added
    SdfGenerator (Runtime/FontCore/SdfGenerator.cs): Burst-compiled IJobParallelFor that generates single-channel SDF tiles using contour-seeded vector propagation (8SSEDT). Operates on raw quadratic Bezier curves — no bitmap rasterization.
  • Added
    MsdfGenerator (Runtime/FontCore/MsdfGenerator.cs): Burst-compiled IJobParallelFor that generates multi-channel SDF tiles in RGBAHalf format. Three per-channel seed+propagate passes with tangent carry for pseudo-distance encoding, plus a fourth channel-agnostic error correction pass.
  • Added
    SdfCore (Runtime/FontCore/SdfCore.cs): Shared types and reference implementations of SDF/MSDF algorithms — GlyphTask struct (used by both generators), tile transforms, Y-monotone splitting, winding number computation, 8SSEDT propagation (with and without tangent), Newton refinement, and quadratic solver. Both SdfJob and MsdfJob inline their own copies of the algorithms for optimal Burst codegen.
  • Added
    GlyphCurveCache (Runtime/FontCore/GlyphCurveCache.cs): Per-font lazy extraction of glyph outlines as quadratic Bezier segments via FreeType OutlineDecompose. Normalizes curves to [0,1] glyph space, computes per-contour winding, runs edge coloring, and sorts segments by Y. Includes a thread-safe FreeType face pool for parallel extraction.
  • Added
    EdgeColoring (Runtime/FontCore/EdgeColoring.cs): Port of msdfgen's edgeColoringSimple — assigns per-edge RGB channel masks for MSDF rendering. Detects corners via cross/dot product thresholds and cycles colors at corner vertices. Computes bisector vectors and corner flags for each segment.
  • Added
    RenderMode enum on UniText component: SDF (single-channel) or MSDF (multi-channel) — controls which atlas mode the component uses.
  • Added
    SDF Detail Multiplier on UniTextFont: Controls tile size classification — higher values force larger atlas tiles for fonts with thin strokes (e.g. calligraphic).
  • Added
    Glyph Overrides on UniTextFont: Per-glyph tile size overrides (Auto/64/128/256) for fine-tuning quality on specific glyphs.
  • Added
    FontFamily struct on UniTextFontStack: families[] array replaces old flat fonts + variants lists. Each family has a primary font and optional faces[] (bold, italic, light, etc.) with a pre-computed FontFaceLookup for fast weight/style matching.
  • Added
    FontFaceLookup: Sorted weight arrays, variable font slots (upright + italic), CSS §5.2 weight matching via BinarySearch. Pre-computed at initialization.
  • Added
    Variable font support: VariationModifier with <var> tag for direct axis control (wght, wdth, ital, slnt, opsz). UniTextFont.VariableAxes exposes axis metadata. IsVariable property. Variable font axis enumeration via HarfBuzz (hb_ot_var_get_axis_infos) and variation setting via hb_font_set_variations.
  • Added
    Three-tier face resolution in ResolveFontFaces(): (1) Variable font axes — if font has wght/ital/slnt, set axes directly; (2) Static font face — CSS §5.2 weight matching via FontFaceLookup.FindFace(); (3) Synthesis — fake bold/italic buffers remain non-zero for shader-based synthesis.
  • Added
    <b>/<i> semantic tags: Automatically resolve to variable axes when available, fall back to static faces, then to synthesis. <var> tag provides direct axis control without fallback.
  • Added
    CSS font-weight scale for bold: BoldModifier uses weight scale 100-900 encoded as a byte per codepoint. Smart default: max(700, baseWeight + 300). Explicit parameter: <b=500> for CSS weight 500. Fake bold applied via SDF shader dilate (UV1.y) and per-glyph advance correction using FreeType's embolden ratio (em/24).
  • Added
    Variation run tracking: VariationRunInfo struct and variationMap dictionary in TextProcessor track per-run axis values. Shaper.Shape() accepts HB.hb_variation_t[] parameter. FreeType coordinates set via FT.SetVarDesignCoordinates().
  • Added
    FaceInfo auto-population (editor): familyName, styleName, weightClass, and isItalic are automatically extracted from font data via FreeType on OnEnable/OnValidate and kept in sync. Fields are read-only in the inspector.
  • Added
    Native variable font API: HarfBuzz axis enumeration/variation setting and FreeType Multiple Masters support (FT.GetMMVar, FT.SetVarDesignCoordinates) in FT.cs and HB.cs.
  • Added
    WordSegmentationProcessor (Runtime/Unicode/WordBreak/WordSegmentationProcessor.cs): Post-processes UAX#14 line breaks — dispatches contiguous SA-class script runs (Thai, Lao, etc.) to registered word segmenters.
  • Added
    BestPathSegmenter (Runtime/Unicode/WordBreak/BestPathSegmenter.cs): Dictionary-based best-path (maximal matching) DP algorithm — same approach as ICU Thai. Inserts Optional break opportunities at word boundaries.
  • Added
    DoubleArrayTrie (Runtime/Unicode/WordBreak/DoubleArrayTrie.cs): Read-only compact double-array trie for fast dictionary lookup. Thread-safe after construction.
  • Added
    WordSegmentationDictionary (Runtime/Unicode/WordBreak/WordSegmentationDictionary.cs): ScriptableObject holding compiled trie data for a specific script. Configured via UniTextSettings.dictionaries[].
  • Added
    Dictionary Builder tab in UniText Tools window: Builds dictionary assets from word list text files. Supports drag-and-drop, multi-file selection, target script selection, and automatic trie compilation.
  • Added
    EffectModifier (Runtime/ModCore/EffectModifier.cs): Abstract base class for modifiers that render an additional effect pass behind the face. Registers EffectPass (apply/revert callbacks) on the mesh generator. Provides RecordEffectGlyph() to store per-glyph UV and offset data, and ApplyToMesh()/RevertFromMesh() to write effect data to UV2 channel with vertex position offsets.
  • Added
    OutlineModifier (Runtime/ModCore/Modifiers/OutlineModifier.cs): Outline effect via <outline=dilate>, <outline=#color>, or <outline=dilate,#color>. Supports fixed pixel size mode. Defaults: dilate=0.2, color=black.
  • Added
    ShadowModifier (Runtime/ModCore/Modifiers/ShadowModifier.cs): Shadow/underlay effect via <shadow=#color>, <shadow=dilate,#color>, or <shadow=dilate,#color,offsetX,offsetY,softness>. Supports vertex shifts for offset shadows and fixed pixel size mode. Defaults: dilate=0, color=black 50% alpha.
  • Added
    EffectPacking (Runtime/Core/EffectPacking.cs): Static utility for packing Color32 into a single float via bit reinterpretation for shader unpacking.
  • Added
    UV2/UV3 buffers on UniTextMeshGenerator: On-demand allocation of additional UV channels for effect layer data.
  • Added
    Multi-pass rendering in UniText.UpdateSubMeshes: Effect passes rendered before the face pass using separate materials (Base shader). Each pass applies and reverts its mesh modifications via callbacks.
  • Added
    UniTextMaterialCache (Runtime/Core/UniTextMaterialCache.cs): Static cache that lazily creates and manages shared materials — SDF Face, SDF Base, MSDF Face, MSDF Base. MSDF variants use the UNITEXT_MSDF shader keyword. Subscribes to atlas texture changes and syncs _MainTex automatically.
  • Added
    Shader references on UniTextSettings: requiredShaders[] array stores references to Base, Face, and Emoji shaders. GetShader(int index) provides runtime access. Settings provider auto-populates these on editor load.
  • Added
    TagRule (Runtime/ModCore/Rules/TagRule.cs): Universal configurable tag parse rule that replaces all individual per-tag rule classes. A single sealed class with a serialized tagName field. Supports defaultParameter for fallback values and automatic parameter merging (tag-supplied values take priority, remaining fields filled from default).
  • Added
    MarkdownWrapRule (Runtime/ModCore/Rules/MarkdownWrapRule.cs): Parse rule for Markdown-style symmetric wrap markers (**, *, ~~, ++). Configurable marker string, stack-based open/close matching, priority by marker length.
  • Added
    Simplified TagParseRule base: Parameters are now always optional (no HasParameter virtual). Self-closing is purely syntax-driven (<tag/> or <tag=value/>). Removed HasParameter, IsSelfClosing, InsertString virtual properties.
  • Added
    DeprecatedTagRules (Runtime/ModCore/Rules/DeprecatedTagRules.cs): All 16 tag parse rule classes (14 old + 2 new for outline/shadow) consolidated as hidden one-liner definitions marked with [HideFromTypeSelector] for backward-compatible deserialization.
  • Added
    Selector (Editor/Selector.cs): Full-featured searchable popup selector with grouped mode (expandable group headers with submenu panels), flat search mode (multi-word tokenized, case-insensitive), keyboard navigation, description panels, theme-aware icons, auto-close on focus loss, and optional search field toggle.
  • Added
    Mod Register Presets: The modifier list in the UniText inspector now opens a Selector with ~30 predefined presets (Bold, Italic, Outline, Shadow, Markdown variants, etc.) with icons and descriptions. Presets auto-configure both modifier and parse rule.
  • Added
    RangeRuleDataDrawer (Editor/RangeRuleDataDrawer.cs): Custom property drawer for RangeRule.Data that generates structured UI for modifier parameters based on ParameterFieldAttribute metadata. Supports float, int, color, bool, string, enum, and unit (px/em/%) field types.
  • Added
    UniTextFontStackEditor (Editor/UniTextFontStackEditor.cs): Custom inspector for UniTextFontStack with a Font Families section — each family displayed as a foldable group with primary font, faces list, family name mismatch warnings, weight/italic labels, add/remove buttons, and drag-and-drop zone.
  • Added
    Glyph Picker in font editor: Type text to preview glyph rendering, select individual glyphs, and add tile size overrides directly from the preview grid.
  • Added
    Variable Axes Info in font editor: Displays detected variable font axis metadata (tag, name, min/default/max) when a variable font is loaded.
  • Added
    UniTextObjectMenu (Editor/UniTextObjectMenu.cs): GameObject/UI/ menu items for creating UniText Text and Button objects. Supports prefab overrides via UniTextSettings. Creates Canvas/EventSystem if needed.
  • Added
    Atlas preview tabs: Font editor preview split into SDF, MSDF, and Emoji tabs. Uses a Hidden/UniText/AtlasPreview shader to display raw distance field textures (grayscale for SDF, RGB for MSDF) from Texture2DArray slices.
  • Added
    Theme-aware editor icons: UniTextEditorResources provides tinted icon caching for dark/light theme, with per-group and per-type icon mappings.
  • Added
    Text selection highlight: DefaultTextHighlighter gains a selectionGraphic for programmatic text selection display via SetSelection()/ClearSelection().
  • Added
    ParameterFieldAttribute (Runtime/Attributes/ParameterFieldAttribute.cs): Declares modifier parameter metadata (order, name, type, default) for auto-generating editor UI. Applied to all parameterized modifiers.
  • Added
    TypeDescriptionAttribute (Runtime/Attributes/TypeDescriptionAttribute.cs): Human-readable description for types, shown in the Selector popup. Applied to all modifiers and parse rules.
  • Added
    HideFromTypeSelectorAttribute (Runtime/Attributes/TypeSelectorAttribute.cs): Hides a type from the type selector dropdown while keeping it deserializable.
  • Added
    virtualPositionedGlyphs buffer on UniTextBuffers: Separate buffer for glyphs injected by modifiers (ellipsis dots, list markers). Does not affect hit testing or selection.
  • Added
    BeforeGenerateMesh event on UniText: Raised after glyph positioning but before mesh generation, allowing modifiers to inject virtual glyphs.
  • Added
    EllipsisModifier and ListModifier now inject PositionedGlyph entries into the virtual buffer instead of drawing directly during mesh generation.
  • Added
    UniTextWorld (Runtime/Core/Component/UniTextWorld.cs): World-space text rendering component. Provides the same text processing pipeline as UniText (Unicode, BiDi, shaping, line breaking, modifiers, emoji, font fallback, variable fonts) but renders via MeshRenderer + MeshFilter instead of CanvasRenderer. No Canvas required.
  • Added
    UniTextBase (Runtime/Core/Component/UniTextBase.cs): Extracted shared base class from UniText — all text processing, modifier management, dirty flags, lifecycle, and parallel batch pipeline now live in UniTextBase. Both UniText (Canvas) and UniTextWorld (MeshRenderer) inherit from it.
  • Added
    UniTextBase_Parallel (Runtime/Core/Component/UniTextBase_Parallel.cs): Extracted parallel batch processing pipeline (component collection, glyph batching, atlas rasterization, mesh generation, apply) from UniText_Parallel into a shared base partial class.
  • Added
    Per-instance owned sub-meshes: Each effect pass and face segment renders via a dedicated child GameObject (-_UTWSM_-) with its own MeshFilter + MeshRenderer + per-instance Mesh (HideFlags.HideAndDontSave). Sorting order controls render layering (effects behind face).
  • Added
    Phased mesh upload: Base vertex data (positions, UV0, UV1, UV3, colors, triangles) written once to all SDF sub-meshes; effect passes then overwrite only changed channels (UV2 + vertex shifts). Skips Mesh.Clear() when vertex count is unchanged between frames.
  • Added
    UniTextWorldEditor (Editor/UniTextWorldEditor.cs): Custom inspector for UniTextWorld with sorting order and sorting layer controls.
  • Added
    UniTextBaseEditor (Editor/UniTextBaseEditor.cs): Extracted shared editor base class from UniTextEditor for reuse by both UniTextEditor and UniTextWorldEditor.
  • Added
    SmallCapsModifier (Runtime/ModCore/Modifiers/SmallCapsModifier.cs): Renders lowercase letters as small capitals. Two-tier approach: (1) Native — activates OpenType smcp feature via HarfBuzz for proper small cap glyphs; (2) Synthesis — converts to uppercase and scales down by 0.8x (fallback for fonts without smcp). Per-codepoint attribute byte: 0 = unchanged, 1 = native, 2 = synthesis. Synthesis adjusts both vertex positions and shaped glyph advances.
  • Added
    LowercaseModifier (Runtime/ModCore/Modifiers/LowercaseModifier.cs): Transforms text to lowercase within marked ranges. Applied during modifier Apply phase before shaping.
  • Added
    smcp feature detection in Shaper: HasSmcpFeature() test-shapes 'a' with and without smcp feature, compares glyph IDs. Result cached per font ID in smcpSupportCache.
  • Added
    HarfBuzz feature support: hb_feature_t struct and Shape(font, buffer, features) overload for passing OpenType features to shaping. MakeTag() utility for constructing OpenType tag values.
  • Added
    Shaper features parameter: Shaper.Shape() now accepts optional hb_feature_t[] for per-run OpenType feature activation (used by SmallCaps for smcp).
  • Added
    UI Creation Prefabs on UniTextSettings: textPrefab and buttonPrefab fields for customizing GameObject/UI/ menu item creation.
  • Added
    FreeType OutlineDecompose: New native API that decomposes glyph outlines into quadratic Bezier segments in design units, replacing the old SDF bitmap rendering path.
  • Added
    FaceInfo extensions: Added weightClass (CSS 100-900 from OS/2 usWeightClass) and isItalic (from FreeType style_flags) to the FaceInfo struct.
  • Added
    DefaultParameterAttribute (Runtime/Attributes/DefaultParameterAttribute.cs): Declares default parameter values for modifiers, enabling parameter auto-fill in the editor.
  • Added
    ParameterFieldUtility (Editor/ParameterFieldUtility.cs): Extracted shared parameter field drawing logic from RangeRuleDataDrawer for reuse by DefaultParameterDrawer and other editors.
  • Added
    Emoji atlas Texture2DArray: EmojiFont now maintains a Texture2DArray synced from staging Texture2D pages, with incremental dirty-page sync.
  • Added
    ColorParsing (Runtime/ModCore/ColorParsing.cs): Shared static utility for parsing hex (#RGB, #RRGGBB, #RRGGBBAA) and 21 named colors. Extracted from ColorModifier for reuse by OutlineModifier, ShadowModifier, and RangeRuleDataDrawer.
  • Changed
    UniText component refactored: shared logic (text processing, modifier management, dirty flags, lifecycle, parallel pipeline) extracted to UniTextBase. UniText retains only Canvas-specific rendering (CanvasRenderer, stencil, UpdateGeometry).
  • Changed
    UniText_Parallel refactored: batch pipeline logic extracted to UniTextBase_Parallel. UniText_Parallel retains only Canvas-specific click handling.
  • Changed
    Mesh generator callbacks renamed to camelCase: OnGlyphonGlyph, OnAfterPageonAfterPage, OnRebuildStartonRebuildStart, OnRebuildEndonRebuildEnd.
  • Changed
    Mesh generator: removed unused public fields (currentShapedGlyphIndex, x, y, width, xScale, atlasSize, gradientScale, spreadRatio, rectWidth, hAlignment, currentFontId). SetHorizontalAlignment() method removed.
  • Changed
    UniTextFontProvider: renamed MainFontPrimaryFont, MainFontIdPrinaryFontId. Internal field names updated accordingly.
  • Changed
    EmojiFont: emoji atlas textures now use mipmaps (Texture2D and Texture2DArray created with mipmap=true). Filter mode changed to Trilinear with mipMapBias = -0.5f. Packing spacing increased from 1 to 4 pixels to prevent mipmap bleeding.
  • Changed
    All modifier base classes updated to use renamed UniTextBase references instead of UniText.
  • Changed
    Mesh generator rewritten from group-by-font-then-atlas iteration to single-pass loop over all positioned glyphs. SDF glyphs look up tiles in the shared GlyphAtlas; emoji glyphs processed separately in GenerateEmojiSegment.
  • Changed
    UV encoding changed: UV0.zw = (tileIdx, glyphH) for atlas tile lookup; UV1 = (aspect, faceDilate) as Vector2 (was Vector4).
  • Changed
    Glyph metrics now use design units directly throughout the pipeline — removed pointSize-based metricsConversion factor.
  • Changed
    UniTextRenderData simplified to carry only mesh and font ID; materials assigned externally via UniTextMaterialCache.
  • Changed
    Multi-pass effect rendering in UpdateSubMeshes: effect passes render before the face pass, each with apply/revert callbacks modifying UV2 and vertex positions.
  • Changed
    Required canvas shader channels extended to include TexCoord2 and TexCoord3 for effect layers.
  • Changed
    Glyph reference counting: UniText component tracks currentGlyphKeys and calls AddRef/Release on the atlas, enabling accurate eviction.
  • Changed
    Atlas pre-allocation: estimated tile area per atlas mode calculated before rendering, enabling GlyphAtlas.PreAllocate().
  • Changed
    Periodic atlas maintenance: page recycling every 60 frames, atlas shrinking every 300 frames.
  • Changed
    Mesh generator glyph lookup changed from fontHash (int) to varHash48 (long) — supports variable font axis variation. variationMap from buffers used to resolve per-run variation hashes.
  • Changed
    UniTextFont no longer owns atlas textures — all atlas management delegated to GlyphAtlas singletons.
  • Changed
    Glyph preparation/rendering pipeline rewritten: PrepareGlyphBatch filters via GlyphAtlas.TryGetEntry and protects existing entries with AddRef; RenderPreparedBatch extracts curves via GlyphCurveCache (supports parallel extraction); PackRenderedBatch queues segments to GlyphAtlas.EnsureGlyph.
  • Changed
    CreateFontAsset() simplified — removed samplingPointSize, spreadStrength, renderMode, atlasSize parameters.
  • Changed
    ClearDynamicData() disposes curve cache and clears font entries from the shared atlas instead of destroying per-font textures.
  • Changed
    OnDestroy() now calls Shaper.ClearCache() to properly release HarfBuzz native data (was previously leaking).
  • Changed
    FaceInfo.pointSize removed; replaced by weightClass and isItalic fields.
  • Changed
    HarfBuzz memory: Shaper.FontCacheEntry now pins the managed byte[] via GCHandle instead of copying to unmanaged memory via Marshal.AllocHGlobal, eliminating the duplicate font data in memory.
  • Changed
    Glyph lookup key changed from uint glyphIndex to long glyphKey (48-bit variation hash + glyph index) via GlyphAtlas.MakeKey(varHash48, glyphIndex). Enables the same font to cache different glyph shapes for different variable font axis values.
  • Changed
    PrepareGlyphBatch and RenderPreparedBatch now accept varHash48 and ftCoords parameters for variable font rendering. FreeType design coordinates set before glyph extraction.
  • Changed
    Removed Appearance property and GetMaterials() method from UniTextFontProvider.
  • Changed
    Constructor no longer takes an appearance parameter.
  • Changed
    Constructor now calls BuildResolvedFamilies() to flatten the entire fallback chain into a resolvedFamilies[] array with fontIdToFamilyIndex dictionary for O(1) family lookup.
  • Changed
    HasVariants/FindVariant() replaced by HasFaces property, GetFamilyIndex(int fontId) and GetFamilyLookup(ushort familyIndex) for direct access to FontFaceLookup.
  • Changed
    Font batch key changed from UniTextFont reference to (UniTextFont, RenderModee, varHash48) struct — variable font runs with different axis values are batched separately.
  • Changed
    Glyph collection no longer filters already-atlased glyphs at collection time.
  • Changed
    RasterizeGlyphBatches extracted as a separate method with per-batch timing diagnostics.
  • Changed
    DoGenerateMeshData now clears virtual glyphs buffer, invokes BeforeGenerateMesh, and passes virtual glyphs alongside regular glyphs to GenerateMeshDataOnly.
  • Changed
    PeriodicAtlasMaintenance() extracted as a separate static method, called before component processing instead of after.
  • Changed
    BaseLineModifier refactored: line segment computation extracted into ComputeLineSegments(), executed once then rendered per page. No longer restricted to matching the current font. Event hook changed from OnAfterGlyphsPerFont to OnAfterPage.
  • Changed
    LineRenderHelper rewritten from 3-quad atlas-based rendering (12 vertices) to 1-quad tile-based rendering (4 vertices) using GlyphAtlas.TryGetEntry for underscore glyph lookup.
  • Changed
    EllipsisModifier changed from immediate mesh drawing (GlyphRenderHelper.DrawString) to virtual glyph injection into virtualPositionedGlyphs. Event hook changed from OnAfterGlyphsPerFont to BeforeGenerateMesh.
  • Changed
    ListModifier changed from immediate mesh drawing to virtual glyph injection, same pattern as EllipsisModifier. Parameter separator changed from : to ,.
  • Changed
    LineHeightModifier parameter format changed from s:value to s,value (comma-separated).
  • Changed
    ColorModifier color parsing logic extracted to shared ColorParsing utility class.
  • Changed
    ItalicModifier now skips vertex shear when the resolved font is already natively italic (FaceInfo.isItalic).
  • Changed
    BoldModifier ParameterField format changed from "int" to "int(100,900)" for range-constrained editor UI.
  • Changed
    UniTextFontToolsWindow renamed to UniTextToolsWindow; menu item changed to Tools/UniText Tools. File list refactored into reusable DrawFileList() method.
  • Changed
    Font editor: removed Atlas Settings section (point size, atlas size, spread, render mode). Replaced with Settings section (font scale, SDF detail multiplier). Atlas preview changed from per-font Texture2D to shared Texture2DArray slices.
  • Changed
    Type selector dropdown replaced by Selector popup with icons, descriptions, and group navigation.
  • Changed
    Editor resource path changed from Icons/{name} to UniText/Icons/{name}.
  • Changed
    Settings provider no longer draws defaultAppearance; now draws UI Creation Prefabs and Word Segmentation sections.
  • Changed
    EmojiFont material shader changed from UI/Default to UniText/Emoji (via UniTextSettings.GetShader).
  • Changed
    SearchableSelector renamed to Selector (file and class). Added showSearch parameter to Show() for hiding the search field.
  • Changed
    Font editor: added Apply/Revert buttons for rebuild-required properties (sdfDetailMultiplier, glyphOverrides). Changes are staged as pending until explicitly applied.
  • Changed
    RangeRuleDataDrawer: shared parameter field drawing logic extracted into ParameterFieldUtility for reuse.
  • Removed
    UniTextAppearance (Runtime/FontCore/UniTextAppearance.cs): Deleted. ScriptableObject that mapped fonts to rendering materials with per-frame property delta caching. Material management replaced by UniTextMaterialCache.
  • Removed
    SDF rendering classes from FreeTypeParallel (Runtime/FontCore/FreeTypeParallel.cs): SdfRenderedGlyph struct and SdfGlyphRenderer class removed. FreeTypeFacePool rewritten — SDF bitmap rendering via FT.RenderSdfGlyph() removed, class retained for color bitmap/emoji rendering only. SDF generation replaced by curve-based GlyphCurveCache + Burst SDF/MSDF jobs.
  • Removed
    GlyphRenderHelper (Runtime/ModCore/Modifiers/GlyphRenderHelper.cs): Deleted. Immediate glyph mesh generation utility (DrawGlyph, DrawString, MeasureString). Replaced by virtual glyph injection pattern.
  • Removed
    UniTextRenderMode enum (Runtime/FontCore/FontTypes.cs): Removed (had values: SDF, Smooth, Mono). Replaced by UniText.RenderModee enum (SDF, MSDF) on the component.
  • Removed
    AtlasMode enum (Runtime/FontCore/GlyphAtlas.cs): Removed. GlyphAtlas.GetInstance() now takes UniText.RenderModee directly.
  • Removed
    Per-font atlas textures: atlasTextures, atlasSize, spreadStrength, atlasRenderMode, usedGlyphRects, freeGlyphRects, and shelf packing state removed from UniTextFont.
  • Removed
    FreeType SDF native API: ut_ft_set_sdf_spread, ut_ft_render_sdf_glyph, ut_ft_free_sdf_buffer P/Invoke declarations and wrappers removed from FT.cs.
  • Removed
    Shader GUIs: UniText_BitmapShaderGUI.cs and UniText_SDFShaderGUI.cs deleted (old custom ShaderGUI for bitmap and SDF shader inspectors).
  • Removed
    Individual tag parse rule files (14 files): BoldParseRule.cs, ItalicParseRule.cs, ColorParseRule.cs, SizeParseRule.cs, UnderlineParseRule.cs, StrikethroughParseRule.cs, CSpaceParseRule.cs, LineSpacingParseRule.cs, LineHeightParseRule.cs, GradientParseRule.cs, EllipsisTagRule.cs, ObjParseRule.cs, Link/LinkTagParseRule.cs, UppercaseParseRule.cs. All consolidated into TagRule with backward-compatible stubs in DeprecatedTagRules.cs.
  • Removed
    GeneratedMeshSegment struct: Removed from UniTextMeshGenerator. Replaced by EffectPass struct for multi-pass rendering.
  • Removed
    defaultAppearance from UniTextSettings and its backup system.
  • Removed
    GlyphsByFont grouping from SharedPipelineComponents (no longer needed with single-pass mesh generation).
  • Removed
    sourceFontFilePath from UniTextFont.
  • Removed
    fonts and variants from UniTextFontStack: Flat StyledList<UniTextFont> fonts list and UniTextFont[] variants array replaced by FontFamily[] families.
  • Removed
    FindClosestVariant() from UniTextFontStack: Replaced by FontFaceLookup.FindFace() with CSS §5.2 directional weight matching.
  • Removed
    CurrentAtlasMode property from UniText: Removed. GlyphAtlas.GetInstance() now takes RenderMode directly.
  • Removed
    Zstd-compressed font data: Font bytes stored in UniTextFont assets are now compressed with Zstandard (level 22) at import time. Decompression is lazy (on first FontData access) with zero per-frame cost. Benchmarks: ~600 MB/s on desktop, ~175 MB/s on low-end Android. Typical Latin font (600 KB) decompresses in <1 ms. Build size reduction: ~2.7x for Latin/Arabic fonts, ~1.3x for CJK fonts.
  • Removed
    Zstd native integration: Decompression (ut_zstd_decompress, ut_zstd_get_frame_content_size) built into the runtime unitext_native library across all platforms (Windows, Linux, macOS, Android, iOS, tvOS, WebGL). Runtime library built with -DZSTD_BUILD_COMPRESSION=OFF for minimal size (~80 KB).
  • Removed
    Editor-only compression: ut_zstd_compress and ut_zstd_compress_bound live in unitext_native_editor (desktop only). Zstd.Compress() is available only under #if UNITY_EDITOR.
  • Removed
    Automatic migration: OnValidate detects uncompressed font data via Zstd magic bytes (0x28B52FFD) and compresses in-place. No manual migration step needed.
  • Removed
    Memory optimization: In runtime builds, compressed fontData is freed after decompression to avoid keeping both copies in memory.
  • Removed
    Burst dependency: Added com.unity.burst >= 1.6.0 to package dependencies.
  • Fixed
    HarfBuzz memory leak on font destroy: UniTextFont.OnDestroy() now calls Shaper.ClearCache() to release HarfBuzz native data (unmanaged font copy, hb_blob, hb_face, hb_font). Previously, these resources leaked in the static fontCache until domain reload.
  • Fixed
    Duplicate font data in memory: HarfBuzz FontCacheEntry now pins the managed byte[] via GCHandle instead of allocating a separate unmanaged copy, halving per-font memory overhead.
  • Fixed
    FontSize minimum too restrictive: fontSize, minFontSize, maxFontSize setters clamped to 1f minimum, preventing small text in world-space. Changed minimum to 0.01f.
  • Fixed
    UniTextSettings resilience: Fixed settings loss on package reinstallation.
  • Fixed
    Unity 2021/2022 compatibility: Fixed compiler errors for older Unity versions.