aboutsummaryrefslogtreecommitdiffstats
path: root/text/grapheme.c
blob: b2eb1367e8f3004e63ade0ad87a7f5cd868c2fc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* Grapheme clusters (UAX #29, Unicode 15.1): text/text.h. One pass, the
 * rules in order; three pieces of state for the rules that look further
 * back than one character (GB9c, GB11, GB12-13). */
#include "text.h"

void vega_graphemes(const uint32_t *codes, size_t count, uint8_t *breaks) {
    breaks[0] = count > 0; /* GB1 */
    /* conjunct: 1 after a consonant and extends, 2 once a linker came too
     * (GB9c); pictograph: 1 after a pictograph and extends, 2 after its ZWJ
     * (GB11); regional: indicators in a row (GB12, GB13). */
    unsigned conjunct = 0, pictograph = 0, regional = 0, before = 0;
    for (size_t i = 0; i < count; ++i) {
        uint8_t property = vega_grapheme(codes[i]);
        unsigned now = property & 15, incb = property >> 4;
        bool pictographic = vega_extended_pictographic(codes[i]);
        if (i) {
            bool join;
            if (before == VEGA_GCB_CR && now == VEGA_GCB_LF) join = true;                       /* GB3 */
            else if (before == VEGA_GCB_CONTROL || before == VEGA_GCB_CR || before == VEGA_GCB_LF) join = false; /* GB4 */
            else if (now == VEGA_GCB_CONTROL || now == VEGA_GCB_CR || now == VEGA_GCB_LF) join = false;          /* GB5 */
            else if (before == VEGA_GCB_L && (now == VEGA_GCB_L || now == VEGA_GCB_V || now == VEGA_GCB_LV || now == VEGA_GCB_LVT))
                join = true;                                                                   /* GB6 */
            else if ((before == VEGA_GCB_LV || before == VEGA_GCB_V) && (now == VEGA_GCB_V || now == VEGA_GCB_T))
                join = true;                                                                   /* GB7 */
            else if ((before == VEGA_GCB_LVT || before == VEGA_GCB_T) && now == VEGA_GCB_T) join = true; /* GB8 */
            else if (now == VEGA_GCB_EXTEND || now == VEGA_GCB_ZWJ) join = true;               /* GB9 */
            else if (now == VEGA_GCB_SPACING_MARK) join = true;                                /* GB9a */
            else if (before == VEGA_GCB_PREPEND) join = true;                                  /* GB9b */
            else if (conjunct == 2 && incb == VEGA_INCB_CONSONANT) join = true;                /* GB9c */
            else if (pictograph == 2 && pictographic) join = true;                             /* GB11 */
            else if (before == VEGA_GCB_RI && now == VEGA_GCB_RI && regional % 2) join = true; /* GB12, GB13 */
            else join = false;                                                                 /* GB999 */
            breaks[i] = !join;
        }
        conjunct = incb == VEGA_INCB_CONSONANT ? 1
                   : conjunct && incb == VEGA_INCB_LINKER ? 2
                   : conjunct && incb == VEGA_INCB_EXTEND ? conjunct : 0;
        pictograph = pictographic ? 1
                     : pictograph == 1 && now == VEGA_GCB_EXTEND ? 1
                     : pictograph == 1 && now == VEGA_GCB_ZWJ ? 2 : 0;
        regional = now == VEGA_GCB_RI ? regional + 1 : 0;
        before = now;
    }
    breaks[count] = count > 0; /* GB2 */
}