diff --git a/unicode/norm/composition.go b/unicode/norm/composition.go index e2087bce..698b5d12 100644 --- a/unicode/norm/composition.go +++ b/unicode/norm/composition.go @@ -506,6 +506,16 @@ func (rb *reorderBuffer) compose() { } } b[k] = b[i] + if b[k].ccc == 0 { + // b[i] is itself a starter, so it becomes the base for what + // follows and blocks any later mark from an earlier starter + // (D115). Updating s only in the combinesBackward branch above + // misses this: a ccc-0 character that combines backward does not + // end the segment, so it reaches here with s still pointing at + // the previous starter, and a following mark then composes + // across it. + s = k + } k++ } rb.nrune = k diff --git a/unicode/norm/normalize_test.go b/unicode/norm/normalize_test.go index 8e27435d..6a52be0a 100644 --- a/unicode/norm/normalize_test.go +++ b/unicode/norm/normalize_test.go @@ -653,6 +653,21 @@ var appendTestsNFC = []AppendTest{ {"\uff9e", grave(30), "\uff9e" + grave(29) + cgj + grave(1)}, {grave(30), "\uff9e", grave(30) + cgj + "\uff9e"}, + { // A ccc=0 character blocks composition (UAX #15, D115) even when + // it combines backward and so cannot end its segment. U+09BE + // composes with U+09C7, but here it only stands between the + // starter and U+0300, which must therefore not compose with "i". + "", + "i\u09be\u0300\u0316", + "i\u09be\u0316\u0300", + }, + { // the same shape with a ccc=0 character that does not combine + // backward, which has always been handled correctly + "", + "i\u0903\u0300\u0316", + "i\u0903\u0316\u0300", + }, + // Tests designed for Iter. { // ordering of non-composing combining characters "",