diff --git a/src/font/general-bidi.c b/src/font/general-bidi.c new file mode 100644 index 00000000..36cddc4b --- /dev/null +++ b/src/font/general-bidi.c @@ -0,0 +1,1761 @@ +/* + * This file is part of MiniGUI, a mature cross-platform windowing + * and Graphics User Interface (GUI) support system for embedded systems + * and smart IoT devices. + * + * Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd. + * Copyright (C) 1998~2002, WEI Yongming + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Or, + * + * As this program is a library, any link to this program must follow + * GNU General Public License version 3 (GPLv3). If you cannot accept + * GPLv3, you need to be licensed from FMSoft. + * + * If you have got a commercial license of this program, please use it + * under the terms and conditions of the commercial license. + * + * For more information about the commercial license, please refer to + * . + */ + +/* +** general-bidi.c: +** +** A general implementation of UNICODE BIDIRECTIONAL ALGORITHM +** +** https://www.unicode.org/reports/tr9/ +** +** Created by WEI Yongming at 2019/03/08 +** +** This implementation is based on LGPL'd Bidi: +** +** https://github.com/gbidi/gbidi +** +** Authors: +** Behdad Esfahbod, 2001, 2002, 2004 +** Dov Grobgeld, 1999, 2000, 2017 +** +** Copyright (C) 2004 Sharif FarsiWeb, Inc +** Copyright (C) 2001,2002 Behdad Esfahbod +** Copyright (C) 1999,2000,2017 Dov Grobgeld +*/ + +#include +#include +#include +#include +#include + +#define DEBUG + +#include "common.h" +#include "minigui.h" +#include "gdi.h" + +#include "devfont.h" + +#include "bidi.h" +#include "general-bidi.h" + +#ifdef DEBUG + +static inline int gbidi_debug_status(void) +{ + return 1; +} + +#include + +static void validate_run_list(BidiRun *run_list) +{ + register BidiRun *q; + + assert (run_list); + assert (run_list->next); + assert (run_list->next->prev == run_list); + assert (run_list->type == BIDI_TYPE_SENTINEL); + for_run_list (q, run_list) { + assert (q->next); + assert (q->next->prev == q); + } + assert (q == run_list); +} + +#else + +static inline void validate_run_list(BidiRun *run_list) +{ +} + +#endif /* !DEBUG */ + +static BidiRun* new_run(void) +{ + register BidiRun *run; + + run = malloc (sizeof (BidiRun)); + + if (run) { + run->len = run->pos = run->level = run->isolate_level = 0; + run->next = run->prev = run->prev_isolate = run->next_isolate = NULL; + } + + return run; +} + +static BidiRun* new_run_list(void) +{ + register BidiRun *run; + + run = new_run (); + + if (run) { + run->type = BIDI_TYPE_SENTINEL; + run->level = BIDI_SENTINEL; + run->pos = BIDI_SENTINEL; + run->len = BIDI_SENTINEL; + run->next = run->prev = run; + } + + return run; +} + +static void free_run_list(BidiRun *run_list) +{ + if (!run_list) + return; + + validate_run_list (run_list); + + { + register BidiRun *pp; + + pp = run_list; + pp->prev->next = NULL; + while (pp) + { + register BidiRun *p; + + p = pp; + pp = pp->next; + free (p); + }; + } +} + + +static BidiRun* run_list_encode_bidi_types (const BidiType *bidi_types, + const Uint8 *bracket_types, const int len) +{ + BidiRun *list, *last; + register BidiRun *run = NULL; + int i; + + /* Create the list sentinel */ + list = new_run_list (); + if (!list) return NULL; + last = list; + + /* Scan over the character types */ + for (i = 0; i < len; i++) + { + register BidiType char_type = bidi_types[i]; + register Uint8 bracket_type = BIDICHAR_BRACKET_NONE; + if (bracket_types) + bracket_type = bracket_types[i]; + + if (char_type != last->type + /* Always separate bracket into single char runs! */ + || bracket_type != BIDICHAR_BRACKET_NONE + || last->bracket_type != BIDICHAR_BRACKET_NONE + || BIDI_IS_ISOLATE(char_type)) { + run = new_run (); + if (!run) break; + run->type = char_type; + run->pos = i; + last->len = run->pos - last->pos; + last->next = run; + run->prev = last; + run->bracket_type = bracket_type; + last = run; + } + } + + /* Close the circle */ + last->len = len - last->pos; + last->next = list; + list->prev = last; + + if (!run) { + /* Memory allocation failed */ + free_run_list (list); + return NULL; + } + + validate_run_list (list); + + return list; +} + +/* override the run list 'base', with the runs in the list 'over', to + reinsert the previously-removed explicit codes (at X9) from + 'explicits_list' back into 'type_rl_list' for example. This is used at the + end of I2 to restore the explicit marks, and also to reset the character + types of characters at L1. + + it is assumed that the 'pos' of the first element in 'base' list is not + more than the 'pos' of the first element of the 'over' list, and the + 'pos' of the last element of the 'base' list is not less than the 'pos' + of the last element of the 'over' list. these two conditions are always + satisfied for the two usages mentioned above. + + Note: + frees the over list. + + Todo: + use some explanatory names instead of p, q, ... + rewrite comment above to remove references to special usage. +*/ +static BOOL shadow_run_list(BidiRun *base, BidiRun *over, BOOL preserve_length) +{ + register BidiRun *p = base, *q, *r, *s, *t; + register int pos = 0, pos2; + BOOL status = FALSE; + + validate_run_list (base); + validate_run_list (over); + + for_run_list (q, over) + { + if (!q->len || q->pos < pos) continue; + pos = q->pos; + while (p->next->type != BIDI_TYPE_SENTINEL && p->next->pos <= pos) + p = p->next; + /* now p is the element that q must be inserted 'in'. */ + pos2 = pos + q->len; + r = p; + while (r->next->type != BIDI_TYPE_SENTINEL && r->next->pos < pos2) + r = r->next; + if (preserve_length) + r->len += q->len; + /* now r is the last element that q affects. */ + if (p == r) + { + /* split p into at most 3 intervals, and insert q in the place of + the second interval, set r to be the third part. */ + /* third part needed? */ + if (p->pos + p->len > pos2) + { + r = new_run (); + if (!r) goto out; + p->next->prev = r; + r->next = p->next; + r->level = p->level; + r->isolate_level = p->isolate_level; + r->type = p->type; + r->len = p->pos + p->len - pos2; + r->pos = pos2; + } + else + r = r->next; + + if (p->pos + p->len >= pos) + { + /* first part needed? */ + if (p->pos < pos) + /* cut the end of p. */ + p->len = pos - p->pos; + else + { + t = p; + p = p->prev; + free (t); + } + } + } + else + { + if (p->pos + p->len >= pos) + { + /* p needed? */ + if (p->pos < pos) + /* cut the end of p. */ + p->len = pos - p->pos; + else + p = p->prev; + } + + /* r needed? */ + if (r->pos + r->len > pos2) + { + /* cut the beginning of r. */ + r->len = r->pos + r->len - pos2; + r->pos = pos2; + } + else + r = r->next; + + /* remove the elements between p and r. */ + for (s = p->next; s != r;) + { + t = s; + s = s->next; + free (t); + } + } + /* before updating the next and prev runs to point to the inserted q, + we must remember the next element of q in the 'over' list. + */ + t = q; + q = q->prev; + delete_node (t); + p->next = t; + t->prev = p; + t->next = r; + r->prev = t; + } + status = TRUE; + + validate_run_list (base); + +out: + free_run_list (over); + + return status; +} + +/* Some convenience macros */ +#define RL_TYPE(list) ((list)->type) +#define RL_LEN(list) ((list)->len) +#define RL_LEVEL(list) ((list)->level) + +/* "Within this scope, bidirectional types EN and AN are treated as R" */ +#define RL_TYPE_AN_EN_AS_RTL(list) ( \ + (((list)->type == BIDI_TYPE_AN) || ((list)->type == BIDI_TYPE_EN) | \ + ((list)->type == BIDI_TYPE_RTL)) ? BIDI_TYPE_RTL : (list)->type) +#define RL_BRACKET_TYPE(list) ((list)->bracket_type) +#define RL_ISOLATE_LEVEL(list) ((list)->isolate_level) + +#define LOCAL_BRACKET_SIZE 16 + +/* Pairing nodes are used for holding a pair of open/close brackets as + described in BD16. */ +typedef struct _BidiPairingNode { + BidiRun *open; + BidiRun *close; + struct _BidiPairingNode *next; +} BidiPairingNode; + +static BidiRun* merge_with_prev(BidiRun *second) +{ + BidiRun *first; + + first = second->prev; + + first->next = second->next; + first->next->prev = first; + RL_LEN (first) += RL_LEN (second); + + if (second->next_isolate) + second->next_isolate->prev_isolate = first; + first->next_isolate = second->next_isolate; + + free(second); + return first; +} + +static void compact_list(BidiRun *list) +{ + if (list->next) { + for_run_list (list, list) { + if (RL_TYPE (list->prev) == RL_TYPE (list) + && RL_LEVEL (list->prev) == RL_LEVEL (list) + /* Don't join brackets! */ + && RL_BRACKET_TYPE(list) == BIDICHAR_BRACKET_NONE + && RL_BRACKET_TYPE(list->prev) == BIDICHAR_BRACKET_NONE) { + list = merge_with_prev (list); + } + } + } +} + +static void compact_neutrals(BidiRun *list) +{ + if (list->next) { + for_run_list (list, list) { + if (RL_LEVEL (list->prev) == RL_LEVEL (list) + && + ((RL_TYPE (list->prev) == RL_TYPE (list) + || (BIDI_IS_NEUTRAL (RL_TYPE (list->prev)) + && BIDI_IS_NEUTRAL (RL_TYPE (list))))) + /* Don't join brackets! */ + && RL_BRACKET_TYPE(list) == BIDICHAR_BRACKET_NONE + && RL_BRACKET_TYPE(list->prev) == BIDICHAR_BRACKET_NONE) { + list = merge_with_prev (list); + } + } + } +} + +/* + * Search for an adjacent run in the forward or backward direction. + * It uses the next_isolate and prev_isolate run for short circuited searching. + */ + +/* The static sentinel is used to signal the end of an isolating sequence */ +static BidiRun sentinel = { + NULL, NULL, NULL, NULL, 0, 0, BIDI_TYPE_SENTINEL, -1, -1, + BIDICHAR_BRACKET_NONE, +}; + +static BidiRun *get_adjacent_run(BidiRun *list, + BOOL forward, BOOL skip_neutral) +{ + BidiRun *ppp = forward ? list->next_isolate : list->prev_isolate; + if (!ppp) + return &sentinel; + + while (ppp) { + BidiType ppp_type = RL_TYPE (ppp); + + if (ppp_type == BIDI_TYPE_SENTINEL) + break; + + /* Note that when sweeping forward we continue one run + beyond the PDI to see what lies behind. When looking + backwards, this is not necessary as the leading isolate + run has already been assigned the resolved level. */ + if (ppp->isolate_level > list->isolate_level + || (forward && ppp_type == BIDI_TYPE_PDI) + || (skip_neutral && !BIDI_IS_STRONG(ppp_type))) { + ppp = forward ? ppp->next_isolate : ppp->prev_isolate; + if (!ppp) + ppp = &sentinel; + + continue; + } + + break; + } + + return ppp; +} + +#ifdef DEBUG +/*====================================================================== + * For debugging, define some functions for printing the types and the + * levels. + *----------------------------------------------------------------------*/ +static const char char_from_level_array[] = { + /* -1 == BIDI_SENTINEL, indicating start or end of string. */ + '$', + /* 0-61 == 0-9,a-z,A-Z are the the only valid levels before resolving + * implicits. after that the level @ may be appear too. */ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', + + /* TBD - insert another 125-64 levels */ + + /* 62 == only must appear after resolving implicits. */ + '@', + + /* 63 == BIDI_LEVEL_INVALID, internal error, + this level shouldn't be seen. */ + '!', + + /* >= 64 == overflows, this levels and higher + * levels show a real bug!. */ + '*', '*', '*', '*', '*' +}; + +#define gbidi_char_from_level(level) char_from_level_array[(level) + 1] + +static const char *gbidi_get_bidi_type_name(BidiType t) +{ + switch ((int)t) { +# define _BIDI_ADD_TYPE(TYPE,SYMBOL) case BIDI_TYPE_##TYPE: return #TYPE; +# define _BIDI_ALL_TYPES + _BIDI_ADD_TYPE (LTR, 'L') /* Left-To-Right letter */ + _BIDI_ADD_TYPE (RTL, 'R') /* Right-To-Left letter */ + _BIDI_ADD_TYPE (AL, 'A') /* Arabic Letter */ + _BIDI_ADD_TYPE (EN, '1') /* European Numeral */ + _BIDI_ADD_TYPE (AN, '9') /* Arabic Numeral */ + _BIDI_ADD_TYPE (ES, 'w') /* European number Separator */ + _BIDI_ADD_TYPE (ET, 'w') /* European number Terminator */ + _BIDI_ADD_TYPE (CS, 'w') /* Common Separator */ + _BIDI_ADD_TYPE (NSM, '`') /* Non Spacing Mark */ + _BIDI_ADD_TYPE (BN, 'b') /* Boundary Neutral */ + _BIDI_ADD_TYPE (BS, 'B') /* Block Separator */ + _BIDI_ADD_TYPE (SS, 'S') /* Segment Separator */ + _BIDI_ADD_TYPE (WS, '_') /* WhiteSpace */ + _BIDI_ADD_TYPE (ON, 'n') /* Other Neutral */ + _BIDI_ADD_TYPE (LRE, '+') /* Left-to-Right Embedding */ + _BIDI_ADD_TYPE (RLE, '+') /* Right-to-Left Embedding */ + _BIDI_ADD_TYPE (LRO, '+') /* Left-to-Right Override */ + _BIDI_ADD_TYPE (RLO, '+') /* Right-to-Left Override */ + _BIDI_ADD_TYPE (PDF, '-') /* Pop Directional Flag */ + _BIDI_ADD_TYPE (LRI, '+') /* Left-to-Right Isolate */ + _BIDI_ADD_TYPE (RLI, '+') /* Right-to-Left Isolate */ + _BIDI_ADD_TYPE (FSI, '+') /* First-Strong Isolate */ + _BIDI_ADD_TYPE (PDI, '-') /* Pop Directional Isolate */ + _BIDI_ADD_TYPE (WLTR, 'l') /* Weak Left To Right paragraph */ + _BIDI_ADD_TYPE (WRTL, 'r') /* Weak Right To Left paragraph */ + _BIDI_ADD_TYPE (SENTINEL, '$') /* SENTINEL */ +# undef _BIDI_ALL_TYPES +# undef _BIDI_ADD_TYPE + default: + return "?"; + } +} + +static void print_types_re(const BidiRun *pp) +{ + _DBG_PRINTF (" Run types : "); + for_run_list (pp, pp) { + _DBG_PRINTF ("%d:%d(%s)[%d,%d] ", + pp->pos, pp->len, gbidi_get_bidi_type_name (pp->type), pp->level, pp->isolate_level); + } + _DBG_PRINTF ("\n"); +} + +static void print_resolved_levels(const BidiRun *pp) +{ + _DBG_PRINTF (" Res. levels: "); + for_run_list (pp, pp) + { + register int i; + for (i = RL_LEN (pp); i; i--) + _DBG_PRINTF ("%c", gbidi_char_from_level (RL_LEVEL (pp))); + } + _DBG_PRINTF ("\n"); +} + +static void print_resolved_types(const BidiRun *pp) +{ + _DBG_PRINTF (" Res. types : "); + for_run_list (pp, pp) { + int i; + for (i = RL_LEN (pp); i; i--) + _DBG_PRINTF ("%s ", gbidi_get_bidi_type_name (pp->type)); + } + _DBG_PRINTF ("\n"); +} + +static void print_bidi_string(const BidiType *bidi_types, const int len) +{ + int i; + + _DBG_PRINTF (" Org. types : "); + for (i = 0; i < len; i++) + _DBG_PRINTF ("%s ", gbidi_get_bidi_type_name (bidi_types[i])); + _DBG_PRINTF ("\n"); +} + +static void print_pairing_nodes(BidiPairingNode *nodes) +{ + _DBG_PRINTF ("Pairs: "); + while (nodes) { + _DBG_PRINTF ("(%d, %d) ", nodes->open->pos, nodes->close->pos); + nodes = nodes->next; + } + _DBG_PRINTF ("\n"); +} + +#endif /* DEBUG */ + +/*========================================================================= + * define macros for push and pop the status in to / out of the stack + *-------------------------------------------------------------------------*/ + +/* There are a few little points in pushing into and poping from the status + stack: + 1. when the embedding level is not valid (more than + BIDI_MAX_EXPLICIT_LEVEL=125), you must reject it, and not to push + into the stack, but when you see a PDF, you must find the matching code, + and if it was pushed in the stack, pop it, it means you must pop if and + only if you have pushed the matching code, the over_pushed var counts the + number of rejected codes so far. + + 2. there's a more confusing point too, when the embedding level is exactly + BIDI_MAX_EXPLICIT_LEVEL-1=124, an LRO, LRE, or LRI is rejected + because the new level would be BIDI_MAX_EXPLICIT_LEVEL+1=126, that + is invalid; but an RLO, RLE, or RLI is accepted because the new level is + BIDI_MAX_EXPLICIT_LEVEL=125, that is valid, so the rejected codes + may be not continuous in the logical order, in fact there are at most two + continuous intervals of codes, with an RLO, RLE, or RLI between them. To + support this case, the first_interval var counts the number of rejected + codes in the first interval, when it is 0, means that there is only one + interval. +*/ + +/* a. If this new level would be valid, then this embedding code is valid. + Remember (push) the current embedding level and override status. + Reset current level to this new level, and reset the override status to + new_override. + b. If the new level would not be valid, then this code is invalid. Don't + change the current level or override status. +*/ + +#define PUSH_STATUS \ + do { \ + if (over_pushed == 0 \ + && isolate_overflow == 0 \ + && new_level <= BIDI_MAX_EXPLICIT_LEVEL) \ + { \ + if (level == BIDI_MAX_EXPLICIT_LEVEL - 1) \ + first_interval = over_pushed; \ + status_stack[stack_size].level = level; \ + status_stack[stack_size].isolate_level = isolate_level; \ + status_stack[stack_size].isolate = isolate; \ + status_stack[stack_size].override = override; \ + stack_size++; \ + level = new_level; \ + override = new_override; \ + } else if (isolate_overflow == 0) \ + over_pushed++; \ + } while(0) + +/* If there was a valid matching code, restore (pop) the last remembered + (pushed) embedding level and directional override. +*/ +#define POP_STATUS \ + do { \ + if (stack_size) \ + { \ + if (over_pushed > first_interval) \ + over_pushed--; \ + else \ + { \ + if (over_pushed == first_interval) \ + first_interval = 0; \ + stack_size--; \ + level = status_stack[stack_size].level; \ + override = status_stack[stack_size].override; \ + isolate = status_stack[stack_size].isolate; \ + isolate_level = status_stack[stack_size].isolate_level; \ + } \ + } \ + } while(0) + + +/* Return the type of previous run or the SOR, if already at the start of + a level run. */ +#define PREV_TYPE_OR_SOR(pp) \ + ( \ + RL_LEVEL(pp->prev) == RL_LEVEL(pp) ? \ + RL_TYPE(pp->prev) : \ + BIDI_LEVEL_TO_DIR(MAX(RL_LEVEL(pp->prev), RL_LEVEL(pp))) \ + ) + +/* Return the type of next run or the EOR, if already at the end of + a level run. */ +#define NEXT_TYPE_OR_EOR(pp) \ + ( \ + RL_LEVEL(pp->next) == RL_LEVEL(pp) ? \ + RL_TYPE(pp->next) : \ + BIDI_LEVEL_TO_DIR(MAX(RL_LEVEL(pp->next), RL_LEVEL(pp))) \ + ) + + +/* Return the embedding direction of a link. */ +#define BIDI_EMBEDDING_DIRECTION(link) \ + BIDI_LEVEL_TO_DIR(RL_LEVEL(link)) + +int gbidi_get_par_direction(const BidiType *bidi_types, const int len) +{ + register int i; + + for (i = 0; i < len; i++) + if (BIDI_IS_LETTER(bidi_types[i])) + return BIDI_IS_RTL(bidi_types[i]) ? BIDI_PGDIR_RTL : BIDI_PGDIR_LTR; + + return BIDI_PGDIR_ON; +} + +/* Push a new entry to the pairing linked list */ +static BidiPairingNode * pairing_nodes_push(BidiPairingNode *nodes, + BidiRun *open, BidiRun *close) +{ + BidiPairingNode *node = malloc(sizeof(BidiPairingNode)); + node->open = open; + node->close = close; + node->next = nodes; + nodes = node; + return nodes; +} + +/* Sort by merge sort */ +static void pairing_nodes_front_back_split(BidiPairingNode *source, + BidiPairingNode **front, BidiPairingNode **back) +{ + BidiPairingNode *pfast, *pslow; + if (!source || !source->next) { + *front = source; + *back = NULL; + } + else { + pslow = source; + pfast = source->next; + while (pfast) { + pfast= pfast->next; + if (pfast) { + pfast = pfast->next; + pslow = pslow->next; + } + } + + *front = source; + *back = pslow->next; + pslow->next = NULL; + } +} + +static BidiPairingNode * pairing_nodes_sorted_merge(BidiPairingNode *nodes1, + BidiPairingNode *nodes2) +{ + BidiPairingNode *res = NULL; + if (!nodes1) + return nodes2; + if (!nodes2) + return nodes1; + + if (nodes1->open->pos < nodes2->open->pos) { + res = nodes1; + res->next = pairing_nodes_sorted_merge(nodes1->next, nodes2); + } + else { + res = nodes2; + res->next = pairing_nodes_sorted_merge(nodes1, nodes2->next); + } + + return res; +} + +static void sort_pairing_nodes(BidiPairingNode **nodes) +{ + BidiPairingNode *front, *back; + + /* 0 or 1 node case */ + if (!*nodes || !(*nodes)->next) + return; + + pairing_nodes_front_back_split(*nodes, &front, &back); + sort_pairing_nodes(&front); + sort_pairing_nodes(&back); + *nodes = pairing_nodes_sorted_merge(front, back); +} + +static void free_pairing_nodes(BidiPairingNode *nodes) +{ + while (nodes) + { + BidiPairingNode *p = nodes; + nodes = nodes->next; + free(p); + } +} + +BidiLevel gbidi_get_paragraph_els_ex(const BidiType *bidi_types, + const Uint8 *bracket_types, const int len, + int *pbase_dir, BidiLevel *embedding_levels) +{ + BidiLevel base_level_per_iso_level[BIDI_MAX_EXPLICIT_LEVEL]; + BidiLevel base_level, max_level = 0; + int base_dir; + BidiRun *main_run_list = NULL, *explicits_list = NULL, *pp; + BOOL status = FALSE; + int max_iso_level = 0; + + if (!len) { + status = TRUE; + goto out; + } + + _DBG_PRINTF ("in gbidi_get_paragraph_els"); + + /* Determinate character types */ + { + /* Get run-length encoded character types */ + main_run_list = run_list_encode_bidi_types (bidi_types, bracket_types, len); + if (!main_run_list) goto out; + } + + /* Find base level */ + /* If no strong base_dir was found, resort to the weak direction + that was passed on input. */ + base_level = BIDI_DIR_TO_LEVEL (*pbase_dir); + if (!BIDI_IS_STRONG (*pbase_dir)) + /* P2. P3. Search for first strong character and use its direction as + base direction */ + { + int valid_isolate_count = 0; + for_run_list (pp, main_run_list) + { + if (RL_TYPE(pp) == BIDI_TYPE_PDI) + { + /* Ignore if there is no matching isolate */ + if (valid_isolate_count>0) + valid_isolate_count--; + } + else if (BIDI_IS_ISOLATE(RL_TYPE(pp))) + valid_isolate_count++; + else if (valid_isolate_count==0 && BIDI_IS_LETTER (RL_TYPE (pp))) + { + base_level = BIDI_DIR_TO_LEVEL (RL_TYPE (pp)); + *pbase_dir = BIDI_LEVEL_TO_DIR (base_level); + break; + } + } + } + base_dir = BIDI_LEVEL_TO_DIR (base_level); + _DBG_PRINTF (" base level : %c", gbidi_char_from_level (base_level)); + _DBG_PRINTF (" base dir : %s", gbidi_get_bidi_type_name (base_dir)); + + base_level_per_iso_level[0] = base_level; + +# ifdef DEBUG + if (gbidi_debug_status ()) { + print_types_re (main_run_list); + } +# endif /* DEBUG */ + + /* Explicit Levels and Directions */ + _DBG_PRINTF ("explicit levels and directions"); + { + BidiLevel level, new_level = 0; + int isolate_level = 0; + BidiType override, new_override; + int i; + int stack_size, over_pushed, first_interval; + int valid_isolate_count = 0; + int isolate_overflow = 0; + int isolate = 0; /* The isolate status flag */ + struct + { + BidiType override; /* only LTR, RTL and ON are valid */ + BidiLevel level; + int isolate; + int isolate_level; + } status_stack[BIDI_MAX_RESOLVED_LEVELS]; + BidiRun temp_link; + BidiRun *run_per_isolate_level[BIDI_MAX_RESOLVED_LEVELS]; + + memset(run_per_isolate_level, 0, sizeof(run_per_isolate_level[0]) + * BIDI_MAX_RESOLVED_LEVELS); + + /* explicits_list is a list like main_run_list, that holds the explicit + codes that are removed from main_run_list, to reinsert them later by + calling the shadow_run_list. + */ + explicits_list = new_run_list (); + if (!explicits_list) goto out; + + /* X1. Begin by setting the current embedding level to the paragraph + embedding level. Set the directional override status to neutral, + and directional isolate status to FALSE. + + Process each character iteratively, applying rules X2 through X8. + Only embedding levels from 0 to 123 are valid in this phase. */ + + level = base_level; + override = BIDI_TYPE_ON; + /* stack */ + stack_size = 0; + over_pushed = 0; + first_interval = 0; + valid_isolate_count = 0; + isolate_overflow = 0; + + for_run_list (pp, main_run_list) + { + BidiType this_type = RL_TYPE (pp); + RL_ISOLATE_LEVEL (pp) = isolate_level; + + if (BIDI_IS_EXPLICIT_OR_BN (this_type)) + { + if (BIDI_IS_STRONG (this_type)) + { /* LRE, RLE, LRO, RLO */ + /* 1. Explicit Embeddings */ + /* X2. With each RLE, compute the least greater odd + embedding level. */ + /* X3. With each LRE, compute the least greater even + embedding level. */ + /* 2. Explicit Overrides */ + /* X4. With each RLO, compute the least greater odd + embedding level. */ + /* X5. With each LRO, compute the least greater even + embedding level. */ + new_override = BIDI_EXPLICIT_TO_OVERRIDE_DIR (this_type); + for (i = RL_LEN (pp); i; i--) + { + new_level = + ((level + BIDI_DIR_TO_LEVEL (this_type) + 2) & ~1) - + BIDI_DIR_TO_LEVEL (this_type); + isolate = 0; + PUSH_STATUS; + } + } + else if (this_type == BIDI_TYPE_PDF) + { + /* 3. Terminating Embeddings and overrides */ + /* X7. With each PDF, determine the matching embedding or + override code. */ + for (i = RL_LEN (pp); i; i--) + { + if (stack_size && status_stack[stack_size-1].isolate != 0) + break; + POP_STATUS; + } + } + + /* X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. */ + /* Remove element and add it to explicits_list */ + RL_LEVEL (pp) = BIDI_SENTINEL; + temp_link.next = pp->next; + move_node_before (pp, explicits_list); + pp = &temp_link; + } + else if (this_type == BIDI_TYPE_PDI) + /* X6a. pop the direction of the stack */ + { + for (i = RL_LEN (pp); i; i--) + { + if (isolate_overflow > 0) + { + isolate_overflow--; + RL_LEVEL (pp) = level; + } + + else if (valid_isolate_count > 0) + { + /* Pop away all LRE,RLE,LRO, RLO levels + from the stack, as these are implicitly + terminated by the PDI */ + while (stack_size && !status_stack[stack_size-1].isolate) + POP_STATUS; + over_pushed = 0; /* The PDI resets the overpushed! */ + POP_STATUS; + isolate_level-- ; + valid_isolate_count--; + RL_LEVEL (pp) = level; + RL_ISOLATE_LEVEL (pp) = isolate_level; + } + else + { + /* Ignore isolated PDI's by turning them into ON's */ + RL_TYPE (pp) = BIDI_TYPE_ON; + RL_LEVEL (pp) = level; + } + } + } + else if (BIDI_IS_ISOLATE(this_type)) + { + /* TBD support RL_LEN > 1 */ + new_override = BIDI_TYPE_ON; + isolate = 1; + if (this_type == BIDI_TYPE_LRI) + new_level = level + 2 - (level%2); + else if (this_type == BIDI_TYPE_RLI) + new_level = level + 1 + (level%2); + else if (this_type == BIDI_TYPE_FSI) + { + /* Search for a local strong character until we + meet the corresponding PDI or the end of the + paragraph */ + BidiRun *fsi_pp; + int isolate_count = 0; + int fsi_base_level = 0; + for_run_list (fsi_pp, pp) + { + if (RL_TYPE(fsi_pp) == BIDI_TYPE_PDI) + { + isolate_count--; + if (valid_isolate_count < 0) + break; + } + else if (BIDI_IS_ISOLATE(RL_TYPE(fsi_pp))) + isolate_count++; + else if (isolate_count==0 && BIDI_IS_LETTER (RL_TYPE (fsi_pp))) + { + fsi_base_level = BIDI_DIR_TO_LEVEL (RL_TYPE (fsi_pp)); + break; + } + } + + /* Same behavior like RLI and LRI above */ + if (BIDI_LEVEL_IS_RTL (fsi_base_level)) + new_level = level + 1 + (level%2); + else + new_level = level + 2 - (level%2); + } + + RL_LEVEL (pp) = level; + RL_ISOLATE_LEVEL (pp) = isolate_level++; + base_level_per_iso_level[isolate_level] = new_level; + + if (!BIDI_IS_NEUTRAL (override)) + RL_TYPE (pp) = override; + + if (new_level <= BIDI_MAX_EXPLICIT_LEVEL) + { + valid_isolate_count++; + PUSH_STATUS; + level = new_level; + } + else + isolate_overflow += 1; + } + else if (this_type == BIDI_TYPE_BS) + { + /* X8. All explicit directional embeddings and overrides are + completely terminated at the end of each paragraph. Paragraph + separators are not included in the embedding. */ + break; + } + else + { + /* X6. For all types besides RLE, LRE, RLO, LRO, and PDF: + a. Set the level of the current character to the current + embedding level. + b. Whenever the directional override status is not neutral, + reset the current character type to the directional override + status. */ + RL_LEVEL (pp) = level; + if (!BIDI_IS_NEUTRAL (override)) + RL_TYPE (pp) = override; + } + } + + /* Build the isolate_level connections */ + for_run_list (pp, main_run_list) + { + int isolate_level = RL_ISOLATE_LEVEL (pp); + if (run_per_isolate_level[isolate_level]) + { + run_per_isolate_level[isolate_level]->next_isolate = pp; + pp->prev_isolate = run_per_isolate_level[isolate_level]; + } + run_per_isolate_level[isolate_level] = pp; + } + + /* Implementing X8. It has no effect on a single paragraph! */ + level = base_level; + override = BIDI_TYPE_ON; + stack_size = 0; + over_pushed = 0; + } + /* X10. The remaining rules are applied to each run of characters at the + same level. For each run, determine the start-of-level-run (sor) and + end-of-level-run (eor) type, either L or R. This depends on the + higher of the two levels on either side of the boundary (at the start + or end of the paragraph, the level of the 'other' run is the base + embedding level). If the higher level is odd, the type is R, otherwise + it is L. */ + /* Resolving Implicit Levels can be done out of X10 loop, so only change + of Resolving Weak Types and Resolving Neutral Types is needed. */ + + compact_list (main_run_list); + +# ifdef DEBUG + if (gbidi_debug_status ()) { + print_types_re (main_run_list); + print_bidi_string (bidi_types, len); + print_resolved_levels (main_run_list); + print_resolved_types (main_run_list); + } +# endif /* DEBUG */ + + /* 4. Resolving weak types. Also calculate the maximum isolate level */ + max_iso_level = 0; + _DBG_PRINTF ("resolving weak types"); + { + int last_strong_stack[BIDI_MAX_RESOLVED_LEVELS]; + BidiType prev_type_orig; + BOOL w4; + + last_strong_stack[0] = base_dir; + + for_run_list (pp, main_run_list) + { + register BidiType prev_type, this_type, next_type; + BidiRun *ppp_prev, *ppp_next; + int iso_level; + + ppp_prev = get_adjacent_run(pp, FALSE, FALSE); + ppp_next = get_adjacent_run(pp, TRUE, FALSE); + + this_type = RL_TYPE (pp); + iso_level = RL_ISOLATE_LEVEL(pp); + + if (iso_level > max_iso_level) + max_iso_level = iso_level; + + if (RL_LEVEL(ppp_prev) == RL_LEVEL(pp)) + prev_type = RL_TYPE(ppp_prev); + else + prev_type = BIDI_LEVEL_TO_DIR(MAX(RL_LEVEL(ppp_prev), RL_LEVEL(pp))); + + if (RL_LEVEL(ppp_next) == RL_LEVEL(pp)) + next_type = RL_TYPE(ppp_next); + else + next_type = BIDI_LEVEL_TO_DIR(MAX(RL_LEVEL(ppp_next), RL_LEVEL(pp))); + + if (BIDI_IS_STRONG (prev_type)) + last_strong_stack[iso_level] = prev_type; + + /* W1. NSM + Examine each non-spacing mark (NSM) in the level run, and change the + type of the NSM to the type of the previous character. If the NSM + is at the start of the level run, it will get the type of sor. */ + /* Implementation note: it is important that if the previous character + is not sor, then we should merge this run with the previous, + because of rules like W5, that we assume all of a sequence of + adjacent ETs are in one BidiRun. */ + if (this_type == BIDI_TYPE_NSM) + { + /* New rule in Unicode 6.3 */ + if (BIDI_IS_ISOLATE (RL_TYPE (pp->prev))) + RL_TYPE(pp) = BIDI_TYPE_ON; + + if (RL_LEVEL (ppp_prev) == RL_LEVEL (pp)) + { + if (ppp_prev == pp->prev) + pp = merge_with_prev (pp); + } + else + RL_TYPE (pp) = prev_type; + + if (prev_type == next_type && RL_LEVEL (pp) == RL_LEVEL (pp->next)) + { + if (ppp_next == pp->next) + pp = merge_with_prev (pp->next); + } + continue; /* As we know the next condition cannot be TRUE. */ + } + + /* W2: European numbers. */ + if (this_type == BIDI_TYPE_EN && last_strong_stack[iso_level] == BIDI_TYPE_AL) + { + RL_TYPE (pp) = BIDI_TYPE_AN; + + /* Resolving dependency of loops for rules W1 and W2, so we + can merge them in one loop. */ + if (next_type == BIDI_TYPE_NSM) + RL_TYPE (ppp_next) = BIDI_TYPE_AN; + } + } + + + last_strong_stack[0] = base_dir; + + /* Resolving dependency of loops for rules W4 and W5, W5 may + want to prevent W4 to take effect in the next turn, do this + through "w4". */ + w4 = TRUE; + /* Resolving dependency of loops for rules W4 and W5 with W7, + W7 may change an EN to L but it sets the prev_type_orig if needed, + so W4 and W5 in next turn can still do their works. */ + prev_type_orig = BIDI_TYPE_ON; + + /* Each isolate level has its own memory of the last strong character */ + for_run_list (pp, main_run_list) + { + register BidiType prev_type, this_type, next_type; + int iso_level; + BidiRun *ppp_prev, *ppp_next; + + this_type = RL_TYPE (pp); + iso_level = RL_ISOLATE_LEVEL(pp); + + ppp_prev = get_adjacent_run(pp, FALSE, FALSE); + ppp_next = get_adjacent_run(pp, TRUE, FALSE); + + if (RL_LEVEL(ppp_prev) == RL_LEVEL(pp)) + prev_type = RL_TYPE(ppp_prev); + else + prev_type = BIDI_LEVEL_TO_DIR(MAX(RL_LEVEL(ppp_prev), RL_LEVEL(pp))); + + if (RL_LEVEL(ppp_next) == RL_LEVEL(pp)) + next_type = RL_TYPE(ppp_next); + else + next_type = BIDI_LEVEL_TO_DIR(MAX(RL_LEVEL(ppp_next), RL_LEVEL(pp))); + + if (BIDI_IS_STRONG (prev_type)) + last_strong_stack[iso_level] = prev_type; + + /* W2 ??? */ + + /* W3: Change ALs to R. */ + if (this_type == BIDI_TYPE_AL) + { + RL_TYPE (pp) = BIDI_TYPE_RTL; + w4 = TRUE; + prev_type_orig = BIDI_TYPE_ON; + continue; + } + + /* W4. A single european separator changes to a european number. + A single common separator between two numbers of the same type + changes to that type. */ + if (w4 + && RL_LEN (pp) == 1 && BIDI_IS_ES_OR_CS (this_type) + && BIDI_IS_NUMBER (prev_type_orig) + && prev_type_orig == next_type + && (prev_type_orig == BIDI_TYPE_EN + || this_type == BIDI_TYPE_CS)) + { + RL_TYPE (pp) = prev_type; + this_type = RL_TYPE (pp); + } + w4 = TRUE; + + /* W5. A sequence of European terminators adjacent to European + numbers changes to All European numbers. */ + if (this_type == BIDI_TYPE_ET + && (prev_type_orig == BIDI_TYPE_EN + || next_type == BIDI_TYPE_EN)) + { + RL_TYPE (pp) = BIDI_TYPE_EN; + w4 = FALSE; + this_type = RL_TYPE (pp); + } + + /* W6. Otherwise change separators and terminators to other neutral. */ + if (BIDI_IS_NUMBER_SEPARATOR_OR_TERMINATOR (this_type)) + RL_TYPE (pp) = BIDI_TYPE_ON; + + /* W7. Change european numbers to L. */ + if (this_type == BIDI_TYPE_EN && last_strong_stack[iso_level] == BIDI_TYPE_LTR) + { + RL_TYPE (pp) = BIDI_TYPE_LTR; + prev_type_orig = (RL_LEVEL (pp) == RL_LEVEL (pp->next) ? + BIDI_TYPE_EN : BIDI_TYPE_ON); + } + else + prev_type_orig = PREV_TYPE_OR_SOR (pp->next); + } + } + + compact_neutrals (main_run_list); + +#ifdef DEBUG + if (gbidi_debug_status ()) { + print_resolved_levels (main_run_list); + print_resolved_types (main_run_list); + } +#endif /* DEBUG */ + + /* 5. Resolving Neutral Types */ + + _DBG_PRINTF ("resolving neutral types - N0"); + { + /* BD16 - Build list of all pairs*/ + int num_iso_levels = max_iso_level + 1; + BidiPairingNode *pairing_nodes = NULL; + BidiRun *local_bracket_stack[BIDI_MAX_EXPLICIT_LEVEL][LOCAL_BRACKET_SIZE]; + BidiRun **bracket_stack[BIDI_MAX_EXPLICIT_LEVEL]; + int bracket_stack_size[BIDI_MAX_EXPLICIT_LEVEL]; + int last_level = RL_LEVEL(main_run_list); + int last_iso_level = 0; + + memset(bracket_stack, 0, sizeof(bracket_stack[0])*num_iso_levels); + memset(bracket_stack_size, 0, sizeof(bracket_stack_size[0])*num_iso_levels); + + /* populate the bracket_size. The first LOCAL_BRACKET_SIZE entries + of the stack are one the stack. Allocate the rest of the entries. + */ + { + int iso_level; + for (iso_level=0; iso_level < LOCAL_BRACKET_SIZE; iso_level++) + bracket_stack[iso_level] = local_bracket_stack[iso_level]; + + for (iso_level=LOCAL_BRACKET_SIZE; iso_level < num_iso_levels; iso_level++) + bracket_stack[iso_level] = malloc (sizeof (bracket_stack[0]) + * BIDI_MAX_NESTED_BRACKET_PAIRS); + } + + /* Build the bd16 pair stack. */ + for_run_list (pp, main_run_list) + { + int level = RL_LEVEL(pp); + int iso_level = RL_ISOLATE_LEVEL(pp); + Uint8 brack_prop = RL_BRACKET_TYPE(pp); + + /* Interpret the isolating run sequence as such that they + end at a change in the level, unless the iso_level has been + raised. */ + if (level != last_level && last_iso_level == iso_level) + bracket_stack_size[last_iso_level] = 0; + + if (brack_prop!= BIDICHAR_BRACKET_NONE + && RL_TYPE(pp)==BIDI_TYPE_ON) { + if (BIDI_IS_BRACKET_OPEN(brack_prop)) { + if (bracket_stack_size[iso_level] == + BIDI_MAX_NESTED_BRACKET_PAIRS) + break; + + /* push onto the pair stack */ + bracket_stack[iso_level][bracket_stack_size[iso_level]++] = pp; + } + else { + int stack_idx = bracket_stack_size[iso_level] - 1; + while (stack_idx >= 0) { + Uint8 se_brack_prop = RL_BRACKET_TYPE(bracket_stack[iso_level][stack_idx]); + if (BIDI_BRACKET_ID(se_brack_prop) == BIDI_BRACKET_ID(brack_prop)) + { + bracket_stack_size[iso_level] = stack_idx; + + pairing_nodes = pairing_nodes_push(pairing_nodes, + bracket_stack[iso_level][stack_idx], + pp); + break; + } + stack_idx--; + } + } + } + last_level = level; + last_iso_level = iso_level; + } + + /* The list must now be sorted for the next algo to work! */ + sort_pairing_nodes(&pairing_nodes); + +#ifdef DEBUG + if (gbidi_debug_status ()) { + print_pairing_nodes (pairing_nodes); + } +#endif /* DEBUG */ + + /* Start the N0 */ + { + BidiPairingNode *ppairs = pairing_nodes; + while (ppairs) + { + int iso_level = ppairs->open->isolate_level; + int embedding_level = base_level_per_iso_level[iso_level]; + + /* Find matching strong. */ + BOOL found = FALSE; + BidiRun *ppn; + for (ppn = ppairs->open; ppn!= ppairs->close; ppn = ppn->next) + { + BidiType this_type = RL_TYPE_AN_EN_AS_RTL(ppn); + + /* Calculate level like in resolve implicit levels below to prevent + embedded levels not to match the base_level */ + int this_level = RL_LEVEL (ppn) + + (BIDI_LEVEL_IS_RTL (RL_LEVEL(ppn)) ^ BIDI_DIR_TO_LEVEL (this_type)); + + /* N0b */ + if (BIDI_IS_STRONG (this_type) && this_level == embedding_level) + { + RL_TYPE(ppairs->open) = RL_TYPE(ppairs->close) = this_level%2 ? BIDI_TYPE_RTL : BIDI_TYPE_LTR; + found = TRUE; + break; + } + } + + /* N0c */ + /* Search for any strong type preceding and within the bracket pair */ + if (!found) + { + /* Search for a preceding strong */ + int prec_strong_level = embedding_level; /* TBDov! Extract from Isolate level in effect */ + int iso_level = RL_ISOLATE_LEVEL(ppairs->open); + for (ppn = ppairs->open->prev; ppn->type != BIDI_TYPE_SENTINEL; ppn=ppn->prev) + { + BidiType this_type = RL_TYPE_AN_EN_AS_RTL(ppn); + if (BIDI_IS_STRONG (this_type) && RL_ISOLATE_LEVEL(ppn) == iso_level) + { + prec_strong_level = RL_LEVEL (ppn) + + (BIDI_LEVEL_IS_RTL (RL_LEVEL(ppn)) ^ BIDI_DIR_TO_LEVEL (this_type)); + + break; + } + } + + for (ppn = ppairs->open; ppn!= ppairs->close; ppn = ppn->next) + { + BidiType this_type = RL_TYPE_AN_EN_AS_RTL(ppn); + if (BIDI_IS_STRONG (this_type) && RL_ISOLATE_LEVEL(ppn) == iso_level) + { + /* By constraint this is opposite the embedding direction, + since we did not match the N0b rule. We must now + compare with the preceding strong to establish whether + to apply N0c1 (opposite) or N0c2 embedding */ + RL_TYPE(ppairs->open) = RL_TYPE(ppairs->close) = prec_strong_level % 2 ? BIDI_TYPE_RTL : BIDI_TYPE_LTR; + RL_LEVEL(ppairs->open) = RL_LEVEL(ppairs->close) = prec_strong_level; + found = TRUE; + break; + } + } + } + + ppairs = ppairs->next; + } + + free_pairing_nodes(pairing_nodes); + + if (num_iso_levels >= LOCAL_BRACKET_SIZE) + { + int i; + /* Only need to free the non static members */ + for (i=LOCAL_BRACKET_SIZE; ibracket_type = NoBracket; + compact_neutrals (main_run_list); + } + } + +#ifdef DEBUG + if (gbidi_debug_status ()) + { + print_resolved_levels (main_run_list); + print_resolved_types (main_run_list); + } +#endif /* DEBUG */ + } + + _DBG_PRINTF ("resolving neutral types - N1+N2"); + { + for_run_list (pp, main_run_list) + { + BidiType prev_type, this_type, next_type; + BidiRun *ppp_prev, *ppp_next; + + ppp_prev = get_adjacent_run(pp, FALSE, FALSE); + ppp_next = get_adjacent_run(pp, TRUE, FALSE); + + /* "European and Arabic numbers are treated as though they were R" + BIDI_CHANGE_NUMBER_TO_RTL does this. */ + this_type = BIDI_CHANGE_NUMBER_TO_RTL (RL_TYPE (pp)); + + if (RL_LEVEL(ppp_prev) == RL_LEVEL(pp)) + prev_type = BIDI_CHANGE_NUMBER_TO_RTL (RL_TYPE(ppp_prev)); + else + prev_type = BIDI_LEVEL_TO_DIR(MAX(RL_LEVEL(ppp_prev), RL_LEVEL(pp))); + + if (RL_LEVEL(ppp_next) == RL_LEVEL(pp)) + next_type = BIDI_CHANGE_NUMBER_TO_RTL (RL_TYPE(ppp_next)); + else + next_type = BIDI_LEVEL_TO_DIR(MAX(RL_LEVEL(ppp_next), RL_LEVEL(pp))); + + if (BIDI_IS_NEUTRAL (this_type)) + RL_TYPE (pp) = (prev_type == next_type) ? + /* N1. */ prev_type : + /* N2. */ BIDI_EMBEDDING_DIRECTION (pp); + } + } + + compact_list (main_run_list); + +#ifdef DEBUG + if (gbidi_debug_status ()) { + print_resolved_levels (main_run_list); + print_resolved_types (main_run_list); + } +#endif /* DEBUG */ + + /* 6. Resolving implicit levels */ + _DBG_PRINTF ("resolving implicit levels"); + { + max_level = base_level; + + for_run_list (pp, main_run_list) + { + BidiType this_type; + int level; + + this_type = RL_TYPE (pp); + level = RL_LEVEL (pp); + + /* I1. Even */ + /* I2. Odd */ + if (BIDI_IS_NUMBER (this_type)) + RL_LEVEL (pp) = (level + 2) & ~1; + else + RL_LEVEL (pp) = + level + + (BIDI_LEVEL_IS_RTL (level) ^ BIDI_DIR_TO_LEVEL (this_type)); + + if (RL_LEVEL (pp) > max_level) + max_level = RL_LEVEL (pp); + } + } + + compact_list (main_run_list); + +#ifdef DEBUG + if (gbidi_debug_status ()) { + print_bidi_string (bidi_types, len); + print_resolved_levels (main_run_list); + print_resolved_types (main_run_list); + } +#endif /* DEBUG */ + + /* Reinsert the explicit codes & BN's that are already removed, from the + explicits_list to main_run_list. */ + _DBG_PRINTF ("reinserting explicit codes"); + if (explicits_list->next != explicits_list) { + register BidiRun *p; + register BOOL stat = + shadow_run_list (main_run_list, explicits_list, TRUE); + explicits_list = NULL; + if (!stat) goto out; + + /* Set level of inserted explicit chars to that of their previous + * char, such that they do not affect reordering. */ + p = main_run_list->next; + if (p != main_run_list && p->level == BIDI_SENTINEL) + p->level = base_level; + for_run_list (p, main_run_list) if (p->level == BIDI_SENTINEL) + p->level = p->prev->level; + } + +# ifdef DEBUG + if (gbidi_debug_status ()) { + print_types_re (main_run_list); + print_resolved_levels (main_run_list); + print_resolved_types (main_run_list); + } +# endif /* DEBUG */ + + _DBG_PRINTF ("reset the embedding levels, 1, 2, 3."); + { + register int j, state, pos; + register BidiType char_type; + register BidiRun *p, *q, *list; + + /* L1. Reset the embedding levels of some chars: + 1. segment separators, + 2. paragraph separators, + 3. any sequence of whitespace characters preceding a segment + separator or paragraph separator, and + 4. any sequence of whitespace characters and/or isolate formatting + characters at the end of the line. + ... (to be continued in gbidi_reorder_line()). */ + list = new_run_list (); + if (!list) goto out; + q = list; + state = 1; + pos = len - 1; + for (j = len - 1; j >= -1; j--) + { + /* close up the open link at the end */ + if (j >= 0) + char_type = bidi_types[j]; + else + char_type = BIDI_TYPE_ON; + if (!state && BIDI_IS_SEPARATOR (char_type)) + { + state = 1; + pos = j; + } + else if (state && + !(BIDI_IS_EXPLICIT_OR_SEPARATOR_OR_BN_OR_WS(char_type) + || BIDI_IS_ISOLATE(char_type))) + { + state = 0; + p = new_run (); + if (!p) { + free_run_list (list); + goto out; + } + p->pos = j + 1; + p->len = pos - j; + p->type = base_dir; + p->level = base_level; + move_node_before (p, q); + q = p; + } + } + if (!shadow_run_list (main_run_list, list, FALSE)) goto out; + } + +#ifdef DEBUG + if (gbidi_debug_status ()) { + print_types_re (main_run_list); + print_resolved_levels (main_run_list); + print_resolved_types (main_run_list); + } +#endif /* DEBUG */ + + { + int pos = 0; + for_run_list (pp, main_run_list) + { + register int l; + register BidiLevel level = pp->level; + for (l = pp->len; l; l--) + embedding_levels[pos++] = level; + } + } + + status = TRUE; + +out: + _DBG_PRINTF ("leaving gbidi_get_paragraph_els"); + + if (main_run_list) + free_run_list (main_run_list); + if (explicits_list) free_run_list (explicits_list); + + return status ? max_level + 1 : 0; +} + + +static void +bidi_string_reverse(Achar32 *str, int len) +{ + int i; + + for (i = 0; i < len / 2; i++) { + Achar32 tmp = str[i]; + str[i] = str[len - 1 - i]; + str[len - 1 - i] = tmp; + } +} + +static void index_array_reverse (int *arr, int len) +{ + int i; + + for (i = 0; i < len / 2; i++) { + int tmp = arr[i]; + arr[i] = arr[len - 1 - i]; + arr[len - 1 - i] = tmp; + } +} + +BidiLevel gbidi_reorder_line(Uint32 flags, + const BidiType *bidi_types, int len, + int off, int base_dir, + BidiLevel *embedding_levels, + Achar32 *visual_str, int *map, + void* extra, CB_REVERSE_EXTRA cb_reverse_extra) +{ + BOOL status = FALSE; + BidiLevel max_level = 0; + + if (len == 0) { + status = TRUE; + goto out; + } + + _DBG_PRINTF ("in gbidi_reorder_line"); + + _DBG_PRINTF ("reset the embedding levels, 4. whitespace at the end of line"); + { + register int i; + + /* L1. Reset the embedding levels of some chars: + 4. any sequence of white space characters at the end of the line. */ + for (i = off + len - 1; i >= off && + BIDI_IS_EXPLICIT_OR_BN_OR_WS (bidi_types[i]); i--) + embedding_levels[i] = BIDI_DIR_TO_LEVEL (base_dir); + } + + /* 7. Reordering resolved levels */ + { + register BidiLevel level; + register int i; + + /* Reorder both the outstring and the order array */ + { + if (BIDI_TEST_BITS (flags, BIDI_FLAG_REORDER_NSM)) { + /* L3. Reorder NSMs. */ + for (i = off + len - 1; i >= off; i--) { + if (BIDI_LEVEL_IS_RTL (embedding_levels[i]) + && bidi_types[i] == BIDI_TYPE_NSM) + { + register int seq_end = i; + level = embedding_levels[i]; + + for (i--; i >= off && + BIDI_IS_EXPLICIT_OR_BN_OR_NSM (bidi_types[i]) + && embedding_levels[i] == level; i--) + ; + + if (i < off || embedding_levels[i] != level) { + i++; + _DBG_PRINTF ("warning: NSM(s) at the beginning of level run"); + } + + if (visual_str) { + bidi_string_reverse (visual_str + i, seq_end - i + 1); + } + + if (map) { + index_array_reverse (map + i, seq_end - i + 1); + } + + if (extra && cb_reverse_extra) { + cb_reverse_extra(extra, seq_end - i + 1, i); + } + } + } + } + + /* Find max_level of the line. We don't reuse the paragraph + * max_level, both for a cleaner API, and that the line max_level + * may be far less than paragraph max_level. */ + for (i = off + len - 1; i >= off; i--) + if (embedding_levels[i] > max_level) + max_level = embedding_levels[i]; + + /* L2. Reorder. */ + for (level = max_level; level > 0; level--) { + for (i = off + len - 1; i >= off; i--) { + if (embedding_levels[i] >= level) { + /* Find all stretches that are >= level_idx */ + register int seq_end = i; + for (i--; i >= off && embedding_levels[i] >= level; i--) + ; + + if (visual_str) + bidi_string_reverse (visual_str + i + 1, seq_end - i); + + if (map) + index_array_reverse (map + i + 1, seq_end - i); + + if (extra && cb_reverse_extra) { + cb_reverse_extra(extra, seq_end - i + 1, i); + } + } + } + } + } + } + + status = TRUE; + +out: + + return status ? max_level + 1 : 0; +} diff --git a/src/font/general-bidi.h b/src/font/general-bidi.h new file mode 100644 index 00000000..0a747afa --- /dev/null +++ b/src/font/general-bidi.h @@ -0,0 +1,263 @@ +/* + * This file is part of MiniGUI, a mature cross-platform windowing + * and Graphics User Interface (GUI) support system for embedded systems + * and smart IoT devices. + * + * Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd. + * Copyright (C) 1998~2002, WEI Yongming + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Or, + * + * As this program is a library, any link to this program must follow + * GNU General Public License version 3 (GPLv3). If you cannot accept + * GPLv3, you need to be licensed from FMSoft. + * + * If you have got a commercial license of this program, please use it + * under the terms and conditions of the commercial license. + * + * For more information about the commercial license, please refer to + * . + */ + +/* +** general-bidi.h: +** +** A general implementation of UNICODE BIDIRECTIONAL ALGORITHM +** +** https://www.unicode.org/reports/tr9/ +** +** Created by WEI Yongming at 2019/03/08 +** +** This implementation is based on LGPL'd Bidi: +** +** https://github.com/gbidi/gbidi +** +** Authors: +** Behdad Esfahbod, 2001, 2002, 2004 +** Dov Grobgeld, 1999, 2000, 2017 +** +** Copyright (C) 2004 Sharif FarsiWeb, Inc +** Copyright (C) 2001,2002 Behdad Esfahbod +** Copyright (C) 1999,2000,2017 Dov Grobgeld +*/ + +#ifndef _MGFONT_GENERAL_BIDI_H +#define _MGFONT_GENERAL_BIDI_H + +/* A few macros for working with bits */ +#define BIDI_TEST_BITS(x, mask) (((x) & (mask)) ? 1 : 0) + +#define BIDI_INCLUDE_BITS(x, mask) ((x) | (mask)) + +#define BIDI_EXCLUDE_BITS(x, mask) ((x) & ~(mask)) + +#define BIDI_SET_BITS(x, mask) ((x) |= (mask)) + +#define BIDI_UNSET_BITS(x, mask) ((x) &= ~(mask)) + +#define BIDI_ADJUST_BITS(x, mask, cond) \ + ((x) = ((x) & ~(mask)) | ((cond) ? (mask) : 0)) + +#define BIDI_ADJUST_AND_TEST_BITS(x, mask, cond) \ + BIDI_TEST_BITS(BIDI_ADJUST_BITS((x), (mask), (cond)), (mask)) + +/* The maximum embedding level value assigned by explicit marks */ +#define BIDI_MAX_EXPLICIT_LEVEL 125 + +/* The maximum *number* of different resolved embedding levels: 0-126 */ +#define BIDI_MAX_RESOLVED_LEVELS 127 + +/* The maximum *number* of nested brackets: 0-63 */ +#define BIDI_MAX_NESTED_BRACKET_PAIRS 63 + +#define BIDI_SENTINEL -1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct _BidiRun BidiRun; + +struct _BidiRun { + BidiRun *prev; + BidiRun *next; + + /* Additional links for connecting the isolate tree */ + BidiRun *prev_isolate; + BidiRun *next_isolate; + + int pos, len; + BidiType type; + BidiLevel level; + BidiLevel isolate_level; + Uint8 bracket_type; +}; + +#define swap(a,b) \ + do { \ + void *t; \ + (t) = (a); \ + (a) = (b); \ + (b) = (t); \ + } while(0) + +#define merge_lists(a,b) \ + do { \ + swap((a)->prev->next, (b)->prev->next); \ + swap((a)->prev, (b)->prev); \ + } while(0) + +#define delete_node(x) \ + do { \ + (x)->prev->next = (x)->next; \ + (x)->next->prev = (x)->prev; \ + } while(0) + +#define insert_node_before(x, list) \ + do { \ + (x)->prev = (list)->prev; \ + (list)->prev->next = (x); \ + (x)->next = (list); \ + (list)->prev = (x); \ + } while(0) + +#define move_node_before(x, list) \ + do { \ + if ((x)->prev) { \ + delete_node(x); \ + } \ + insert_node_before((x), (list)); \ + } while(0) + +#define for_run_list(x, list) \ + for ((x) = (list)->next; (x)->type != BIDI_TYPE_SENTINEL; (x) = (x)->next) + +/** + * \fn gbidi_get_paragraph_dir + * \brief get base paragraph direction + * + * This function finds the base direction of a single paragraph, + * as defined by rule P2 of the Unicode Bidirectional Algorithm available at + * http://www.unicode.org/reports/tr9/#P2. + * + * You typically do not need this function as + * gbidi_get_paragraph_els() knows how to compute base direction + * itself, but you may need this to implement a more sophisticated paragraph + * direction handling. Note that you can pass more than a paragraph to this + * function and the direction of the first non-neutral paragraph is returned, + * which is a very good heuristic to set direction of the neutral paragraphs + * at the beginning of text. For other neutral paragraphs, you better use the + * direction of the previous paragraph. + * + * \param bidi_types the pointer to the BidiType array as returned by + * gbidi_get_bidi_types() + * \param len The length of bidi_types + * + * \return Base pargraph direction. No weak paragraph direction is returned, + * only BIDI_PGDIR_LTR, BIDI_PGDIR_RTL, or BIDI_PGDIR_ON. + * + */ +int gbidi_get_paragraph_dir(const BidiType *bidi_types, int len); + +/* gbidi_get_paragraph_els_ex - get bidi embedding levels of a paragraph + * + * This function finds the bidi embedding levels of a single paragraph, + * as defined by the Unicode Bidirectional Algorithm available at + * http://www.unicode.org/reports/tr9/. + * + * This function implements rules P2 to I1 inclusive, and parts 1 to 3 of L1, + * except for rule X9 which is implemented in gbidi_remove_bidi_marks(). + * Part 4 of L1 is implemented in gbidi_reorder_line(). + * + * There are a few macros defined in gbidi-bidi-types.h to work with this + * embedding levels. + * + * \param bidi_types the pointer to the BidiType array as returned by + * gbidi_get_bidi_types() + * \param bracket_types The pointer to a Uint8 which contains the + bracket types as returned by gbidi_get_bracket_types() + * \param len The length of the list. + * \param base_dir requested and resolved paragraph base direction + * \param embedding_levels The pointer to a buffer which will restore + * the embedding levels + * + * \return The Maximum level found plus one, or zero if any error occurred + * (memory allocation failure most probably). + */ +BidiLevel gbidi_get_paragraph_els_ex(const BidiType *bidi_types, + const Uint8* bracket_types, int len, + int *base_dir, BidiLevel *embedding_levels); + +/* gbidi_reorder_line - reorder a line of logical string to visual + * + * This function reorders the characters in a line of text from logical to + * final visual order. + * + * This function implements part 4 of rule L1, and rules + * L2 and L3 of the Unicode Bidirectional Algorithm available at + * http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels. + * + * As a side effect it also sets position maps if not NULL. + * + * You should provide the resolved paragraph direction and embedding levels as + * set by gbidi_get_paragraph_els(). Also note that the embedding + * levels may change a bit. To be exact, the embedding level of any sequence + * of white space at the end of line is reset to the paragraph embedding level + * (That is part 4 of rule L1). + * + * Note that the bidi types and embedding levels are not reordered. + * You can reorder these (or any other) arrays using the map later. + * The user is responsible to initialize map to something sensible, + * like an identity mapping, or pass NULL if no map is needed. + * + * There is an optional part to this function, which is whether non-spacing + * marks for right-to-left parts of the text should be reordered to come after + * their base characters in the visual string or not. + * + * Most rendering engines expect this behavior, but console-based systems + * for example do not like it. This is controlled by the + * BIDI_FLAG_REORDER_NSM flag. The flag is on in BIDI_FLAGS_DEFAULT. + * + * \param flags The reorder flags. + * \param bidi_types the pointer to the BidiType array as returned by + * gbidi_get_bidi_types() + * \param bracket_types The pointer to a Uint8 which contains the + bracket types as returned by gbidi_get_bracket_types() + * \param len The length of the list. + * \param off The input offset of the beginning of the line in the paragraph. + * \param base_dir The resolved paragraph base direction. + * \param embedding_levels The embedding levels, as returned by + gbidi_get_paragraph_els() + * \param visual_str + * \param extra The pointer to the extra array to reorder; can be NULL. + * \param cb_reverse_extra The callback function to reverse the extra array. + * \param map a map of string indices which is reordered to reflect + * where each glyph ends up. + * + * \return Maximum level found in this line plus one, or zero if any error + * occurred (memory allocation failure most probably). + */ +BidiLevel gbidi_reorder_line(Uint32 flags, const BidiType *bidi_types, + int len, int off, int base_dir, BidiLevel *embedding_levels, + Achar32 *visual_str, int *map, + void* extra, CB_REVERSE_EXTRA cb_reverse_extra); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* !_MGFONT_GENERAL_BIDI_H */ +