Skip to content

Releases

v1.2.0

Choose a tag to compare

@rawframe-release rawframe-release released this 05 Nov 10:28
79268ba

1.2.0 (2026-11-05)

Added

  • muniSegmentIterator_FindBreaksAround gives the boundaries around a
    byte offset, the last at or before it and the first after it, without
    reading the text from its start: what Backspace, the arrow keys and a
    click on a word need. It answers as the iterator would, for grapheme
    clusters, words, sentences and lines at every setting, class
    overrides and a word segmenter included, and leaves the iterator as
    it was. In ordinary text it reads a few code points, so on a long
    paragraph of English prose it takes under half a microsecond for
    clusters, words and lines, where stepping a fresh iterator from the
    start takes milliseconds; sentences back up to the last terminator.
    It needs an iterator given its whole text at init
    (#278).

No results change: the Unicode data is still 18.0.0 and
MUNI_RESULTS_VERSION stays 2, so stored normalized keys, skeletons
and verdicts from 1.1.2 stay valid.

v1.1.2

Choose a tag to compare

@rawframe-release rawframe-release released this 05 Nov 07:14
b026e91

1.1.2 (2026-11-05)

Fixed

  • Every internal function that returns bool now carries
    MUNI_NODISCARD, so a compiler warns where the library would drop
    one of their answers, and tools/check_nodiscard.py keeps new ones
    marked. No call dropped one, so nothing a caller sees changes: same
    API, same behavior. The header tests now also check that
    MUNI_NODISCARD is not empty in C23
    (#276).

No results change: the Unicode data is still 18.0.0 and
MUNI_RESULTS_VERSION stays 2, so stored normalized keys, skeletons
and verdicts from 1.1.1 stay valid.

v1.1.1

Choose a tag to compare

@rawframe-release rawframe-release released this 05 Nov 02:02
39c4a2c

1.1.1 (2026-11-04)

Fixed

  • MUNI_NODISCARD was empty under GCC 14 in C23, since GCC 14 reports
    its C23 as __STDC_VERSION__ 202000L and the macro waited for
    202311L, so a program built with GCC 14 could drop any status the
    library returns without a warning. In C the macro now takes
    [[nodiscard]] in any dialect after C17 whose compiler has the
    attribute. A program that drops a status now gets
    -Wunused-result from GCC 14 too, which -Werror makes an error
    (#271).

No results change: the Unicode data is still 18.0.0 and
MUNI_RESULTS_VERSION stays 2, so stored normalized keys, skeletons
and verdicts from 1.1.0 stay valid.

v1.1.0

Choose a tag to compare

@rawframe-release rawframe-release released this 04 Nov 08:49
17bcfff

1.1.0 (2026-11-04)

Added

  • Every call that takes a muniSegmentIterator or a
    muniScriptIterator back now checks it first, and refuses one no
    init call set up with muni_errorInvalid, leaving it as it was: a
    zeroed iterator, one of stray bytes, or one whose counts, flags or
    states are out of range. Such an iterator was undefined behavior
    before, and a forged script iterator could overflow a buffer on the
    stack. muniSegmentIterator_GetLineBreakStrictness and
    muniSegmentIterator_GetWritingSystem give their defaults for it, as
    for NULL. A copy of an initialized iterator is accepted. The
    pointers an iterator keeps (the text, the overrides, the breaker)
    cannot be checked, so an iterator changed there by hand stays outside
    the contract
    (#264,
    #267).

Changed

  • The check costs each iterator call a few nanoseconds: line breaking
    over English prose takes 1 to 2% longer, and up to 20% where every
    call reports a boundary two code points after the last. Neither
    iterator changes size
    (#264).

No results change: the Unicode data is still 18.0.0 and
MUNI_RESULTS_VERSION stays 2, so stored normalized keys, skeletons
and verdicts from 1.0.0 stay valid.

v1.0.0

Choose a tag to compare

@rawframe-release rawframe-release released this 03 Nov 23:33
cc997f1

1.0.0 (2026-11-03)

The API is settled. 1.0.0 has the API and the results of 0.13.0; what
changes is what a caller may rely on from here. The promise is in
What 1.0.0 promises.

Changed

  • Within 1.x no function, type, enum value, field or macro of the
    eleven public headers, harfbuzz.h included, is removed or renamed,
    and no release is breaking: a change that would break 1.x is made
    additive or does not ship. There will be no 2.0.0
    (#257).
  • The numbers of the property enumerations in properties.h are part
    of the API, so data may store them; a value a new Unicode version
    adds goes after the last
    (#257).
  • The CMake packages maul-unicode and maul-unicode-harfbuzz accept
    a request for any earlier 1.y, where 0.x accepted only its own
    version; the shared library's SOVERSION is 1.0, so a program
    built against 1.0 loads any 1.0.z and is built again for 1.1
    (#248).

No results change: the Unicode data is still 18.0.0 and
MUNI_RESULTS_VERSION stays 2, so stored normalized keys, skeletons
and verdicts from 0.13.0 stay valid.

v0.13.0

Choose a tag to compare

@rawframe-release rawframe-release released this 02 Nov 23:32
5462603

0.13.0 (2026-11-02)

Added

  • muniMeasureUtf8 finds the longest prefix of UTF-8 text that ends at
    a boundary of a muniTextUnit and holds at most maxBytes bytes and
    at most maxUnits units, and gives its length in bytes and in units;
    either output may be NULL. It turns a byte offset into a count of
    units, turns a count of units into a byte offset, cuts text to fit a
    buffer, and counts the grapheme clusters of a whole text or finds
    where a count of them ends. A limit that falls inside a code point or
    a cluster ends the prefix at the boundary before it, and a count of
    UTF-16 units that falls inside a surrogate pair ends it before the
    pair. A call that fits whole and asks for no count does not walk the
    text
    (#243).
  • muni_unitUtf16, a new muniTextUnit, counts UTF-16 units: one for
    a code point up to U+FFFF, two for one past it, and one for a maximal
    ill-formed subpart, as its U+FFFD. With muniMeasureUtf8 it maps
    UTF-8 byte offsets to the UTF-16 offsets that AppKit, UIKit and
    Android take, and back
    (#243).

Changed

  • Breaking: on muni_errorCapacity, muniConvertUtf8ToUtf16,
    muniConvertUtf16ToUtf8, muniConvertUtf8ToUtf32 and
    muniConvertUtf32ToUtf8 give the input offset of the first code
    point not written, instead of the input length. The output holds the
    conversion of the input before that offset, always whole code points,
    and neededOut still counts the whole conversion. A caller can keep
    what was written, or convert the rest from the offset, instead of
    converting again with less input until it fits. A strict-mode error
    after the output filled up still returns that error and its offset.
    Code that read the input length from the offset on
    muni_errorCapacity uses its own length
    (#241).

Removed

  • Breaking: muniCutUtf8 is removed. muniMeasureUtf8 with
    maxUnits set to SIZE_MAX gives the same cut; the unit now comes
    before the byte limit. No consumer called it
    (#243).

    0.12.0 0.13.0
    muniCutUtf8(text, length, budget, unit, &cut) muniMeasureUtf8(text, length, unit, budget, SIZE_MAX, &cut, NULL)

v0.12.0

Choose a tag to compare

@rawframe-release rawframe-release released this 02 Nov 09:01
72e4a45

0.12.0 (2026-11-02)

Added

  • muniSegmentIterator_GetWordKind tells what the segment ending at the
    boundary a word iterator last reported holds, as a muniWordKind: no
    word (spaces, punctuation, symbols), a number, letters, kana or
    ideographs, the highest kind among its code points. Word movement and
    double-click selection skip segments of kind muni_wordNone. Letters
    with digits are letters, and a superscript digit such as "²" makes no
    number. ICU's word rule statuses agree on segments of letters or
    digits alone; on a mixed segment ICU reports the last rule it matched,
    so it calls "ab12" a number
    (#231).

Changed

  • Word breaking executes 4.5% to 6.7% more instructions, to keep the
    kind of each segment
    (#231).

v0.11.0

Choose a tag to compare

@rawframe-release rawframe-release released this 02 Nov 06:28
c3bc965

0.11.0 (2026-11-02)

Added

  • muniGetDecomposition writes the full decomposition of one code
    point, canonical (muni_nfd) or compatibility (muni_nfkd), every
    mapping applied, in canonical order; Hangul syllables give their jamo
    and a code point that does not decompose gives itself. It reports the
    count it needs, with muni_errorCapacity when the output is too
    small. MUNI_MAX_DECOMPOSITION (18, for U+FDFA in NFKD), now public
    in normalize.h, always suffices for this Unicode version; a release
    with a new Unicode version may raise it
    (#227).
  • muniDecodeUtf16, the counterpart of muniDecodeUtf8, decodes the
    code point at the start of UTF-16 units: a surrogate pair takes two
    units, anything else one. An unpaired surrogate gives
    muni_errorUtf16Surrogate with U+FFFD and one unit, as
    muniConvertUtf16ToUtf8 replaces it, so a decoding loop always makes
    progress
    (#228).
  • muniCutUtf8 gives the length of the longest prefix of UTF-8 text
    within a byte budget that ends at a boundary of a unit:
    muni_unitCodePoint, where each maximal ill-formed subpart counts as
    one, or muni_unitGraphemeCluster, which keeps a letter with its
    accents, an emoji ZWJ sequence or a flag whole, for text fields and
    preedit strings. The unit is an enumeration so that a later release
    can add words
    (#230).

v0.10.0

Choose a tag to compare

@rawframe-release rawframe-release released this 02 Nov 02:20
86877f5

0.10.0 (2026-11-02)

⚠ BREAKING CHANGES

  • Line iterators take a strictness level and a writing system in place
    of the CJ switch. muniSegmentIterator_EnableCjAsIdeographic(iterator, true) becomes muniSegmentIterator_SetLineBreakStrictness(iterator, muni_lineBreakLoose), which also adds loose's other breaks.
    muniSegmentIterator_IsCjAsIdeographicEnabled is removed;
    muniSegmentIterator_GetLineBreakStrictness reports the level. A new
    iterator breaks as before
    (#221).

Added

  • Line iterators break at the levels of CSS's line-break property:
    strict, normal (the default), loose and anywhere, set with
    muniSegmentIterator_SetLineBreakStrictness before the first break.
    muniSegmentIterator_SetWritingSystem with
    muni_writingChineseJapanese turns on the rules CSS keeps for Chinese
    and Japanese. Loose breaks before small kana and the prolonged sound
    mark (Line_Break=CJ), before iteration marks and between characters
    such as two ellipses; anywhere breaks between any two grapheme
    clusters. Normal and strict keep CJ with what precedes it, as the CSS
    Text 3 Candidate Recommendation Draft of 14 August 2026 says; ICU4C's
    normal rules and earlier CSS drafts break before CJ in normal. The
    default level breaks as before, at nearly the same cost (0.15% more
    instructions on the bench)
    (#221).
  • muniConvertCase takes two more languages. muni_caseGreek
    uppercases Greek as it is written in capitals: accents and breathings
    go, a dialytika stays and appears where a dropped accent kept two
    vowels apart ("Μάιος" to "ΜΑΪΟΣ"), the disjunctive eta "ή" keeps its
    tonos when it stands alone, and an iota subscript becomes a capital
    iota; it follows ICU's Greek rules. muni_caseArmenian uppercases
    the ligature "և" to "ԵՎ" and titlecases it to "Եվ". Lowercasing,
    titlecasing (but the ligature) and folding follow the default rules
    (#223).

Changed

  • The results version is 2: calls to muniConvertCase with
    muni_caseGreek or muni_caseArmenian, which were refused, now give
    results. No result of the earlier languages changes, so stored keys
    and skeletons stay valid; a caller that stores the results version
    sees it move once
    (#223).

v0.9.1

Choose a tag to compare

@rawframe-release rawframe-release released this 01 Nov 21:32
a7e34a4

0.9.1 (2026-11-01)

Changed

  • The headers now say what two more refusals leave.
    muniReorderBidiLine leaves runsOut and countOut as they were on
    muni_errorInvalid, and muniGetResolvedScripts leaves scriptsOut
    and countOut as they were on muni_errorInvalid and on a UTF-8
    error. Both calls always behaved this way
    (#204).
  • base.h now says what the results version tracks: the status and
    offset of the calls it covers, and their output on success, but not
    the bytes and counts a refusal leaves. This is why the change to
    refused skeletons below keeps it at 1
    (#206).

Fixed

  • muniGetBidiClass returns Left_To_Right for every value past
    U+10FFFF, as properties.h says. It returned Boundary_Neutral, the
    class of the noncharacters U+10FFFE and U+10FFFF, which the table
    carried past them. Only a caller that passes a value that is no code
    point sees the change: bidi and skeletons read the class only of
    code points decoded from valid text, so their results and the results
    version are unchanged
    (#209).
  • muniGetSkeleton now stops at the segment that hit the limit when it
    refuses text with muni_errorLimit (a run of combining marks past
    MUNI_MAX_NORMALIZATION_SEGMENT). It writes the skeleton of the text
    shown before that segment, as far as textOut holds it, and
    neededOut gives its length in bytes; nothing from that segment on is
    written. Before, it went on past the run, dropped the marks that did
    not fit, wrote the skeleton of the rest of the text and set
    neededOut to 0. muniNormalize and muniGetNfkcCasefold already
    stopped this way. A skeleton that succeeds is unchanged, and so is
    the results version
    (#203).