summaryrefslogtreecommitdiff
path: root/pango/mini-fribidi/fribidi.c
diff options
context:
space:
mode:
Diffstat (limited to 'pango/mini-fribidi/fribidi.c')
-rw-r--r--pango/mini-fribidi/fribidi.c1286
1 files changed, 942 insertions, 344 deletions
diff --git a/pango/mini-fribidi/fribidi.c b/pango/mini-fribidi/fribidi.c
index 62db6124..902cde31 100644
--- a/pango/mini-fribidi/fribidi.c
+++ b/pango/mini-fribidi/fribidi.c
@@ -1,50 +1,121 @@
/* FriBidi - Library of BiDi algorithm
- * Copyright (C) 1999 Dov Grobgeld
- *
+ * Copyright (C) 1999,2000 Dov Grobgeld, and
+ * Copyright (C) 2001 Behdad Esfahbod.
+ *
* This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library, in a file named COPYING.LIB; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA
+ *
+ * For licensing issues, contact <dov@imagic.weizmann.ac.il> and
+ * <fwpg@sharif.edu>.
*/
+
#include <glib.h>
#include "pango/pango-utils.h"
#include "fribidi_types.h"
+#undef DEBUG
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#ifdef DEBUG
+#define DBG(s) if (fribidi_debug) { fprintf(stderr, s); }
+#else
+#define DBG(s)
+#endif
+
+#ifdef DEBUG
+// for easier test with the reference code only.
+#define MAX_LEVEL 15
+#else
+// default value.
+#define MAX_LEVEL 61
+#endif
/*======================================================================
// Typedef for the run-length list.
//----------------------------------------------------------------------*/
typedef struct _TypeLink TypeLink;
-struct _TypeLink {
+struct _TypeLink
+{
TypeLink *prev;
TypeLink *next;
+
FriBidiCharType type;
- int pos;
- int len;
- int level;
+ gint pos;
+ gint len;
+ gint level;
};
-typedef struct {
+#define FRIBIDI_LEVEL_START -1
+#define FRIBIDI_LEVEL_END -1
+#define FRIBIDI_LEVEL_REMOVED -2
+
+typedef struct
+{
FriBidiChar key;
FriBidiChar value;
-} key_value_t;
+}
+key_value_t;
+
+typedef struct
+{
+ FriBidiCharType override; /* only L, R and N are valid */
+ gint level;
+}
+level_info;
+
+#ifdef DEBUG
+static gboolean fribidi_debug = FALSE;
+#endif
+
+gboolean fribidi_set_debug (gboolean debug)
+{
+#ifdef DEBUG
+ return fribidi_debug = debug;
+#else
+ return 0;
+#endif
+}
+
+#ifdef DEBUG
+static gint
+bidi_string_strlen (FriBidiChar * str)
+{
+ gint len = 0;
+ while (*str++)
+ len++;
+
+ return len;
+}
+#endif
+
+#ifndef USE_SIMPLE_MALLOC
static TypeLink *free_type_links = NULL;
+#endif
-static TypeLink *new_type_link(void)
+static TypeLink *
+new_type_link (void)
{
TypeLink *link;
-
+
+#ifdef USE_SIMPLE_MALLOC
+ link = g_malloc (sizeof (TypeLink));
+#else
if (free_type_links)
{
link = free_type_links;
@@ -55,13 +126,13 @@ static TypeLink *new_type_link(void)
static GMemChunk *mem_chunk = NULL;
if (!mem_chunk)
- mem_chunk = g_mem_chunk_new ("TypeLinkList",
- sizeof (TypeLink),
- sizeof (TypeLink) * 128,
- G_ALLOC_ONLY);
+ mem_chunk = g_mem_chunk_new ("TypeLinkList",
+ sizeof (TypeLink),
+ sizeof (TypeLink) * 128, G_ALLOC_ONLY);
link = g_chunk_new (TypeLink, mem_chunk);
}
+#endif
link->len = 0;
link->pos = 0;
@@ -71,57 +142,65 @@ static TypeLink *new_type_link(void)
return link;
}
-static void free_type_link(TypeLink *link)
+static void
+free_type_link (TypeLink * link)
{
+#ifdef USE_SIMPLE_MALLOC
+ g_free (link);
+#else
link->next = free_type_links;
free_type_links = link;
+#endif
}
-static TypeLink *run_length_encode_types(int *char_type, int type_len)
+static TypeLink *
+run_length_encode_types (FriBidiCharType * char_type, gint type_len)
{
- TypeLink *list = NULL;
- TypeLink *last;
- TypeLink *link;
+ TypeLink *list = NULL, *last, *link;
+ TypeLink current;
+
FriBidiCharType type;
- int len, pos, i;
+ gint len, pos, i;
/* Add the starting link */
- list = new_type_link();
+ list = new_type_link ();
list->type = FRIBIDI_TYPE_SOT;
+ list->level = FRIBIDI_LEVEL_START;
list->len = 0;
list->pos = 0;
last = list;
/* Sweep over the string_types */
- type = -1;
- len = 0;
- pos = -1;
- for (i=0; i<=type_len; i++)
+ current.type = FRIBIDI_LEVEL_START;
+ current.len = 0;
+ current.pos = -1;
+ for (i = 0; i <= type_len; i++)
{
- if (i==type_len || char_type[i] != type)
+ if (char_type[i] != current.type || i == type_len)
{
- if (pos>=0)
+ if (current.pos >= 0)
{
- link = new_type_link();
- link->type = type;
- link->pos = pos;
- link->len = len;
- last->next = link;
- link->prev = last;
+ link = new_type_link ();
+ link->type = current.type;
+ link->pos = current.pos;
+ link->len = current.len;
+ last->next = link;
+ link->prev = last;
last = last->next;
}
- if (i==type_len)
+ if (i == type_len)
break;
- len = 0;
- pos = i;
+ current.len = 0;
+ current.pos = i;
}
- type =char_type[i];
- len++;
+ current.type = char_type[i];
+ current.len++;
}
/* Add the ending link */
- link = new_type_link();
+ link = new_type_link ();
link->type = FRIBIDI_TYPE_EOT;
+ link->level = FRIBIDI_LEVEL_END;
link->len = 0;
link->pos = type_len;
last->next = link;
@@ -130,336 +209,818 @@ static TypeLink *run_length_encode_types(int *char_type, int type_len)
return list;
}
+/* explicits_list is a list like type_rl_list, that holds the explicit
+ codes that are removed from rl_list, to reinsert them later by calling
+ the override_list.
+*/
+static void
+init_list (TypeLink ** start, TypeLink ** end)
+{
+ TypeLink *list = NULL;
+ TypeLink *link;
+
+ /* Add the starting link */
+ list = new_type_link ();
+ list->type = FRIBIDI_TYPE_SOT;
+ list->level = FRIBIDI_LEVEL_START;
+ list->len = 0;
+ list->pos = 0;
+
+ /* Add the ending link */
+ link = new_type_link ();
+ link->type = FRIBIDI_TYPE_EOT;
+ link->level = FRIBIDI_LEVEL_END;
+ link->len = 0;
+ link->pos = 0;
+ list->next = link;
+ link->prev = list;
+
+ *start = list;
+ *end = link;
+}
+
+/* move an element before another element in a list, the list must have a
+ previous element, used to update explicits_list.
+ assuming that p have both prev and next or none of them, also update
+ the list that p is currently in, if any.
+*/
+void
+move_element_before (TypeLink * p, TypeLink * list)
+{
+ if (p->prev)
+ {
+ p->prev->next = p->next;
+ p->next->prev = p->prev;
+ }
+ p->prev = list->prev;
+ list->prev->next = p;
+ p->next = list;
+ list->prev = p;
+}
+
+/* override the rl_list 'base', with the elements in the list 'over', to
+ reinsert the previously-removed explicit codes (at X9) from
+ 'explicits_list' back into 'type_rl_list'. 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.
+
+ TBD: use some explanatory names instead of p, q, ...
+*/
+void
+override_list (TypeLink * base, TypeLink * over)
+{
+ TypeLink *p = base, *q, *r, *s, *t;
+ gint pos = 0, pos2;
+
+ if (!base)
+ base = over;
+ else if (over)
+ {
+ q = over;
+ while (q)
+ {
+ if (!q->len || q->pos < pos)
+ {
+ t = q;
+ q = q->next;
+ free_type_link (t);
+ continue;
+ }
+ pos = q->pos;
+ while (p->next && 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 && r->next->pos < pos2)
+ r = r->next;
+ /* now r is the last element that q affects. */
+ if (p == r)
+ {
+ /* split p into at most 3 interval, and insert q in the place of
+ the second interval, set r to be the third part. */
+ /* third part needed? */
+ if (p->next && p->next->pos == pos2)
+ r = r->next;
+ else
+ {
+ r = new_type_link ();
+ *r = *p;
+ if (r->next)
+ {
+ r->next->prev = r;
+ r->len = r->next->pos - pos2;
+ }
+ else
+ r->len -= pos - p->pos;
+ r->pos = pos2;
+ }
+ /* first part needed? */
+ if (p->prev && p->pos == pos)
+ {
+ t = p;
+ p = p->prev;
+ free_type_link (t);
+ }
+ else
+ p->len = pos - p->pos;
+ }
+ else
+ {
+ /* cut the end of p. */
+ p->len = pos - p->pos;
+ /* if all of p is cut, remove it. */
+ if (!p->len && p->prev)
+ p = p->prev;
+
+ /* cut the begining of r. */
+ r->pos = pos2;
+ if (r->next)
+ r->len = r->next->pos - pos2;
+ /* if all of r is cut, remove it. */
+ if (!r->len && r->next)
+ r = r->next;
+
+ /* remove the elements between p and r. */
+ for (s = p->next; s != r;)
+ {
+ t = s;
+ s = s->next;
+ free_type_link (t);
+ }
+ }
+ /* before updating the next and prev links to point to the inserted q,
+ we must remember the next element of q in the 'over' list.
+ */
+ t = q;
+ q = q->next;
+ p->next = t;
+ t->prev = p;
+ t->next = r;
+ r->prev = t;
+ }
+ }
+}
+
/* Some convenience macros */
#define RL_TYPE(list) (list)->type
#define RL_LEN(list) (list)->len
#define RL_POS(list) (list)->pos
#define RL_LEVEL(list) (list)->level
-static void compact_list(TypeLink *list)
+static void
+compact_list (TypeLink * list)
{
- while(list)
+ if (list->next)
{
- if (list->prev
- && RL_TYPE(list->prev) == RL_TYPE(list))
+ list = list->next;
+ while (list)
{
- TypeLink *next = list->next;
- list->prev->next = list->next;
- list->next->prev = list->prev;
- RL_LEN(list->prev) = RL_LEN(list->prev) + RL_LEN(list);
- free_type_link(list);
- list = next;
- }
- else
- list = list->next;
+ if (RL_TYPE (list->prev) == RL_TYPE (list)
+ && RL_LEVEL (list->prev) == RL_LEVEL (list))
+ {
+ TypeLink *next = list->next;
+ list->prev->next = list->next;
+ list->next->prev = list->prev;
+ RL_LEN (list->prev) += RL_LEN (list);
+ free_type_link (list);
+ list = next;
+ }
+ else
+ list = list->next;
+ }
+ }
+}
+
+static void
+compact_neutrals (TypeLink * list)
+{
+ if (list->next)
+ {
+ list = list->next;
+ while (list)
+ {
+ if (RL_LEVEL (list->prev) == RL_LEVEL (list)
+ &&
+ ((RL_TYPE
+ (list->prev) == RL_TYPE (list)
+ || FRIBIDI_IS_NEUTRAL (RL_TYPE (list->prev))
+ && FRIBIDI_IS_NEUTRAL (RL_TYPE (list)))))
+ {
+ TypeLink *next = list->next;
+ list->prev->next = list->next;
+ list->next->prev = list->prev;
+ RL_LEN (list->prev) += RL_LEN (list);
+ free_type_link (list);
+ list = next;
+ }
+ else
+ list = list->next;
+ }
+ }
+}
+
+/*=======================================================
+// define macros for push and pop the status in to / out of the stack
+//-------------------------------------------------------*/
+
+/* There's some little points in pushing and poping into the status stack:
+ 1. when the embedding level is not valid (more than MAX_LEVEL=61),
+ 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 yet.
+ 2. there's a more confusing point too, when the embedding level is exactly
+ MAX_LEVEL-1=60, an LRO or LRE must be rejected because the new level would
+ be MAX_LEVEL+1=62, that is invalid, but an RLO or RLE must be accepted
+ because the new level is MAX_LEVEL=61, that is valid, so the rejected
+ codes may be not continuous in the logical order, in fact there is at
+ most two continuous intervals of codes, with a RLO or RLE between them.
+ to support the 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 yet.
+*/
+
+/* 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 \
+ { \
+ if (new_level <= MAX_LEVEL) \
+ { \
+ if (level == MAX_LEVEL - 1) \
+ first_interval = over_pushed; \
+ status_stack->level = level; \
+ status_stack->override = override; \
+ status_stack++; \
+ stack_size++; \
+ level = new_level; \
+ override = new_override; \
+ } else \
+ over_pushed++; \
+ }
+
+/* If there was a valid matching code, restore (pop) the last remembered
+ (pushed) embedding level and directional override.
+*/
+#define POP_STATUS \
+ { \
+ if (over_pushed || stack_size) \
+ { \
+ if (over_pushed > first_interval) \
+ over_pushed--; \
+ else \
+ { \
+ if (over_pushed == first_interval) \
+ first_interval = 0; \
+ status_stack--; \
+ stack_size--; \
+ level = status_stack->level; \
+ override = status_stack->override; \
+ } \
+ } \
+ }
+
+/*==========================================================================
+// There was no support for sor and eor in the absence of Explicit Embedding
+// Levels, so define macros, to support them, with as less change as needed.
+//--------------------------------------------------------------------------*/
+
+/* Return the type of previous char or the sor, if already at the start of
+ a run level. */
+#define PREV_TYPE_OR_SOR(pp) \
+ (RL_LEVEL(pp->prev)==RL_LEVEL(pp) ? RL_TYPE(pp->prev) : FRIBIDI_LEVEL_TO_DIR( \
+ (RL_LEVEL(pp->prev)>RL_LEVEL(pp) ? RL_LEVEL(pp->prev) : RL_LEVEL(pp)) \
+ ))
+
+/* Return the type of next char or the eor, if already at the end of
+ a run level. */
+#define NEXT_TYPE_OR_EOR(pp) \
+ (!pp->next ? FRIBIDI_LEVEL_TO_DIR(RL_LEVEL(pp)) : \
+ (RL_LEVEL(pp->next)==RL_LEVEL(pp) ? RL_TYPE(pp->next) : FRIBIDI_LEVEL_TO_DIR( \
+ (RL_LEVEL(pp->next)>RL_LEVEL(pp) ? RL_LEVEL(pp->next) : RL_LEVEL(pp)) \
+ )))
+
+
+/* Return the embedding direction of a link. */
+#define FRIBIDI_EMBEDDING_DIRECTION(list) \
+ FRIBIDI_LEVEL_TO_DIR(RL_LEVEL(list))
+
+#ifdef DEBUG
+/*======================================================================
+// For debugging, define some functions for printing the types and the
+// levels.
+//----------------------------------------------------------------------*/
+
+static guchar char_from_level_array[] = {
+ 'e', /* FRIBIDI_LEVEL_REMOVED, internal error, this level shouldn't be viewed. */
+ '_', /* FRIBIDI_LEVEL_START or _END, indicating start of string and end of string. */
+ /* 0-9,A-F are the only valid levels in debug mode and before resolving
+ implicits. after that the levels X, Y, Z may be appear too. */
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F',
+ 'X', 'Y', 'Z', /* only must appear after resolving implicits. */
+ 'o', 'o', 'o' /* overflows, this levels and higher levels show a bug!. */
+};
+
+#define fribidi_char_from_level(level) char_from_level_array[(level) + 2]
+
+static void
+print_types_re (TypeLink * pp)
+{
+ fprintf (stderr, "Run types : ");
+ while (pp)
+ {
+ fprintf (stderr, "%d:l%d(%s)[%d] ",
+ pp->pos, pp->len, fribidi_type_name (pp->type), pp->level);
+ pp = pp->next;
}
+ fprintf (stderr, "\n");
}
-/* Define a rule macro */
-
-/* Rules for overriding current type */
-#define TYPE_RULE1(old_this, \
- new_this) \
- if (this_type == TYPE_ ## old_this) \
- RL_TYPE(pp) = FRIBIDI_TYPE_ ## new_this; \
-
-/* Rules for current and previous type */
-#define TYPE_RULE2(old_prev, old_this, \
- new_prev, new_this) \
- if ( prev_type == FRIBIDI_TYPE_ ## old_prev \
- && this_type == FRIBIDI_TYPE_ ## old_this) \
- { \
- RL_TYPE(pp->prev) = FRIBIDI_TYPE_ ## new_prev; \
- RL_TYPE(pp) = FRIBIDI_TYPE_ ## new_this; \
- continue; \
- }
-
-/* A full rule that assigns all three types */
-#define TYPE_RULE(old_prev, old_this, old_next, \
- new_prev, new_this, new_next) \
- if ( prev_type == FRIBIDI_TYPE_ ## old_prev \
- && this_type == FRIBIDI_TYPE_ ## old_this \
- && next_type == FRIBIDI_TYPE_ ## old_next) \
- { \
- RL_TYPE(pp->prev) = FRIBIDI_TYPE_ ## new_prev; \
- RL_TYPE(pp) = FRIBIDI_TYPE_ ## new_this; \
- RL_TYPE(pp->next) = FRIBIDI_TYPE_ ## new_next; \
- continue; \
- }
-
-/* For optimization the following macro only assigns the center type */
-#define TYPE_RULE_C(old_prev, old_this, old_next, \
- new_this) \
- if ( prev_type == FRIBIDI_TYPE_ ## old_prev \
- && this_type == FRIBIDI_TYPE_ ## old_this \
- && next_type == FRIBIDI_TYPE_ ## old_next) \
- { \
- RL_TYPE(pp) = FRIBIDI_TYPE_ ## new_this; \
- continue; \
- }
+static void
+print_resolved_levels (TypeLink * pp)
+{
+ fprintf (stderr, "Res. levels: ");
+ while (pp)
+ {
+ gint i;
+ for (i = 0; i < RL_LEN (pp); i++)
+ fprintf (stderr, "%c", fribidi_char_from_level (RL_LEVEL (pp)));
+ pp = pp->next;
+ }
+ fprintf (stderr, "\n");
+}
+
+static void
+print_resolved_types (TypeLink * pp)
+{
+ fprintf (stderr, "Res. types : ");
+ while (pp)
+ {
+ gint i;
+ for (i = 0; i < RL_LEN (pp); i++)
+ fprintf (stderr, "%c", fribidi_char_from_type (pp->type));
+ pp = pp->next;
+ }
+ fprintf (stderr, "\n");
+}
+
+static void
+print_bidi_string (FriBidiChar * str)
+{
+ gint i;
+ fprintf (stderr, "Org. types : ");
+ for (i = 0; i < bidi_string_strlen (str); i++)
+ fprintf (stderr, "%c",
+ fribidi_char_from_type (_pango_fribidi_get_type (str[i])));
+ fprintf (stderr, "\n");
+}
+#endif
/*======================================================================
// This function should follow the Unicode specification closely!
-//
-// It is still lacking the support for <RLO> and <LRO>.
//----------------------------------------------------------------------*/
static void
-fribidi_analyse_string(/* input */
- FriBidiChar *str,
- int len,
- FriBidiCharType *pbase_dir,
- /* output */
- TypeLink **ptype_rl_list,
- gint *pmax_level)
+fribidi_analyse_string ( /* input */
+ FriBidiChar * str,
+ gint len, FriBidiCharType * pbase_dir,
+ /* output */
+ TypeLink ** ptype_rl_list, gint * pmax_level)
{
- int base_level, base_dir;
- int max_level;
- int i;
- int *char_type;
- int prev_last_strong, last_strong;
- TypeLink *type_rl_list, *pp;
+ gint base_level, base_dir;
+ gint max_level;
+ gint i;
+ gint prev_last_strong, last_strong;
+ FriBidiCharType *char_type;
+ TypeLink *type_rl_list, *explicits_list, *explicits_list_end, *pp;
/* Determinate character types */
- char_type = g_new(gint, len);
- for (i=0; i<len; i++)
+ char_type = g_new (FriBidiCharType, len);
+ for (i = 0; i < len; i++)
char_type[i] = _pango_fribidi_get_type (str[i]);
/* Run length encode the character types */
- type_rl_list = run_length_encode_types(char_type, len);
- g_free(char_type);
+ type_rl_list = run_length_encode_types (char_type, len);
+ g_free (char_type);
+
+ init_list (&explicits_list, &explicits_list_end);
/* Find the base level */
- if (*pbase_dir == FRIBIDI_TYPE_L)
+ if (FRIBIDI_IS_STRONG (*pbase_dir))
+ base_level = FRIBIDI_DIR_TO_LEVEL (*pbase_dir);
+ /* P2. P3. Search for first strong character and use its direction as
+ base direction */
+ else
{
- base_dir = FRIBIDI_TYPE_L;
- base_level = 0;
+ base_level = 0; /* Default */
+ base_dir = FRIBIDI_TYPE_ON;
+ for (pp = type_rl_list; pp; pp = pp->next)
+ if (FRIBIDI_IS_LETTER (RL_TYPE (pp)))
+ {
+ base_level = FRIBIDI_DIR_TO_LEVEL (RL_TYPE (pp));
+ base_dir = FRIBIDI_LEVEL_TO_DIR (base_level);
+ break;
+ }
+
+ /* If no strong base_dir was found, resort to the weak direction
+ that was passed on input. */
+ if (FRIBIDI_IS_NEUTRAL (base_dir))
+ base_level = FRIBIDI_DIR_TO_LEVEL (*pbase_dir);
}
- else if (*pbase_dir == FRIBIDI_TYPE_R)
+ base_dir = FRIBIDI_LEVEL_TO_DIR (base_level);
+
+#ifdef DEBUG
+ if (fribidi_debug)
{
- base_dir = FRIBIDI_TYPE_R;
- base_level = 1;
+ fprintf (stderr, "Base level : %c\n",
+ fribidi_char_from_level (base_level));
+ fprintf (stderr, "Base dir : %c\n",
+ fribidi_char_from_type (base_dir));
+ print_types_re (type_rl_list);
}
+#endif
- /* Search for first strong character and use its direction as base
- direciton */
- else
+ /* Explicit Levels and Directions */
+ DBG ("Explicit Levels and Directions.\n");
+ {
+ /* X1. Begin by setting the current embedding level to the paragraph
+ embedding level. Set the directional override status to neutral.
+ Process each character iteratively, applying rules X2 through X9.
+ Only embedding levels from 0 to 61 are valid in this phase. */
+ gint level = base_level;
+ gint override = FRIBIDI_TYPE_ON;
+ gint new_level, new_override;
+ /* stack */
+ gint stack_size = 0;
+ gint over_pushed = 0;
+ gint first_interval = 0;
+ level_info *status_stack = g_new (level_info, MAX_LEVEL + 2);
+ TypeLink *temp_link = new_type_link ();
+
+ gint i;
+
+ for (pp = type_rl_list->next; pp->next; pp = pp->next)
+ {
+ gint this_type = RL_TYPE (pp);
+ if (FRIBIDI_IS_EXPLICIT_OR_BN (this_type))
+ {
+ if (FRIBIDI_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 = FRIBIDI_EXPLICIT_TO_OVERRIDE_DIR (this_type);
+ for (i = 0; i < RL_LEN (pp); i++)
+ {
+ new_level =
+ ((level + FRIBIDI_DIR_TO_LEVEL (this_type) + 2) & ~1) -
+ FRIBIDI_DIR_TO_LEVEL (this_type);
+ PUSH_STATUS;
+ }
+ }
+ else if (this_type == FRIBIDI_TYPE_PDF)
+ {
+ /* 3. Terminating Embeddings and overrides */
+ /* X7. With each PDF, determine the matching embedding or
+ override code. */
+ for (i = 0; i < RL_LEN (pp); i++)
+ POP_STATUS;
+ }
+ /* X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. */
+ /* Remove element and add it to explicits_list */
+ temp_link->next = pp->next;
+ pp->level = FRIBIDI_LEVEL_REMOVED;
+ move_element_before (pp, explicits_list_end);
+ pp = temp_link;
+ }
+ else
+ {
+ /* X6. For all typed 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 (!FRIBIDI_IS_NEUTRAL (override))
+ RL_TYPE (pp) = override;
+ }
+ /* X8. All explicit directional embeddings and overrides are
+ completely terminated at the end of each paragraph. Paragraph
+ separators are not included in the embedding. */
+ /* This function is running on a single paragraph, so we can do
+ X8 after all the input is processed. */
+ }
+
+ /* Implementing X8. It has no effect on a single paragraph! */
+ level = base_level;
+ override = FRIBIDI_TYPE_ON;
+ status_stack -= stack_size;
+ stack_size = 0;
+ over_pushed = 0;
+
+ free_type_link (temp_link);
+ g_free (status_stack);
+ }
+ /* 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 (type_rl_list);
+
+#ifdef DEBUG
+ if (fribidi_debug)
{
- base_level = 0; /* Default */
- base_dir = FRIBIDI_TYPE_N;
- for (pp = type_rl_list; pp; pp = pp->next)
- {
- if (RL_TYPE(pp) == FRIBIDI_TYPE_R)
- {
- base_level = 1;
- base_dir = FRIBIDI_TYPE_R;
- break;
- }
- else if (RL_TYPE(pp) == FRIBIDI_TYPE_L)
- {
- base_level = 0;
- base_dir = FRIBIDI_TYPE_L;
- break;
- }
- }
-
- /* If no strong base_dir was found, resort to the weak direction
- * that was passed on input.
- */
- if (base_dir == FRIBIDI_TYPE_N)
- {
- if (*pbase_dir == FRIBIDI_TYPE_WR)
- {
- base_dir = FRIBIDI_TYPE_RTL;
- base_level = 1;
- }
- else if (*pbase_dir == FRIBIDI_TYPE_WL)
- {
- base_dir = FRIBIDI_TYPE_LTR;
- base_level = 0;
- }
- }
+ print_types_re (type_rl_list);
+ print_bidi_string (str);
+ print_resolved_levels (type_rl_list);
+ print_resolved_types (type_rl_list);
}
-
- /* 1. Explicit Levels and Directions. TBD! */
- compact_list(type_rl_list);
-
- /* 2. Explicit Overrides. TBD! */
- compact_list(type_rl_list);
-
- /* 3. Terminating Embeddings and overrides. TBD! */
- compact_list(type_rl_list);
-
+#endif
+
/* 4. Resolving weak types */
- last_strong = base_dir;
- for (pp = type_rl_list->next; pp->next; pp = pp->next)
- {
- int prev_type = RL_TYPE(pp->prev);
- int this_type = RL_TYPE(pp);
- int next_type = RL_TYPE(pp->next);
-
- /* Remember the last strong character */
- if (prev_type == FRIBIDI_TYPE_AL
- || prev_type == FRIBIDI_TYPE_R
- || prev_type == FRIBIDI_TYPE_L)
+ DBG ("Resolving weak types.\n");
+ {
+ gint last_strong, prev_type_org, w4;
+
+ last_strong = base_dir;
+
+ for (pp = type_rl_list->next; pp->next; pp = pp->next)
+ {
+ gint prev_type, this_type, next_type;
+
+ prev_type = PREV_TYPE_OR_SOR (pp);
+ this_type = RL_TYPE (pp);
+ next_type = NEXT_TYPE_OR_EOR (pp);
+
+ if (FRIBIDI_IS_STRONG (prev_type))
last_strong = prev_type;
-
- /* W1. NSM */
- if (this_type == FRIBIDI_TYPE_NSM)
- {
- if (prev_type == FRIBIDI_TYPE_SOT)
- RL_TYPE(pp) = FRIBIDI_TYPE_N; /* Will be resolved to base dir */
- else
- RL_TYPE(pp) = prev_type;
- }
- /* W2: European numbers */
- if (this_type == FRIBIDI_TYPE_N
- && last_strong == FRIBIDI_TYPE_AL)
- RL_TYPE(pp) = FRIBIDI_TYPE_AN;
-
- /* W3: Change ALs to R
- We have to do this for prev character as we would otherwise
- interfer with the next last_strong which is FRIBIDI_TYPE_AL.
- */
- if (prev_type == FRIBIDI_TYPE_AL)
- RL_TYPE(pp->prev) = FRIBIDI_TYPE_R;
-
- /* 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 (RL_LEN(pp) == 1)
- {
- TYPE_RULE_C(EN,ES,EN, EN);
- TYPE_RULE_C(EN,CS,EN, EN);
- TYPE_RULE_C(AN,CS,AN, AN);
- }
+ /* 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. */
+ if (this_type == FRIBIDI_TYPE_NSM)
+ {
+ RL_TYPE (pp) = prev_type;
+ continue;
+ }
- /* W5. A sequence of European terminators adjacent to European
- numbers changes to All European numbers.
- */
- if (this_type == FRIBIDI_TYPE_ET)
- {
- if (next_type == FRIBIDI_TYPE_EN
- || prev_type == FRIBIDI_TYPE_EN) {
- RL_TYPE(pp) = FRIBIDI_TYPE_EN;
+ /* W2: European numbers. */
+ if (this_type == FRIBIDI_TYPE_EN && last_strong == FRIBIDI_TYPE_AL)
+ {
+ RL_TYPE (pp) = FRIBIDI_TYPE_AN;
+
+ /* Resolving dependency of loops for rules W1 and W2, so we
+ can merge them in one loop. */
+ if (next_type == FRIBIDI_TYPE_NSM)
+ RL_TYPE (pp->next) = FRIBIDI_TYPE_AN;
}
- }
+ }
- /* This type may have been overriden */
- this_type = RL_TYPE(pp);
-
- /* W6. Otherwise change separators and terminators to other neutral */
- if (this_type == FRIBIDI_TYPE_ET
- || this_type == FRIBIDI_TYPE_CS
- || this_type == FRIBIDI_TYPE_ES)
- RL_TYPE(pp) = FRIBIDI_TYPE_ON;
-
- /* W7. Change european numbers to L. */
- if (prev_type == FRIBIDI_TYPE_EN
- && last_strong == FRIBIDI_TYPE_L)
- RL_TYPE(pp->prev) = FRIBIDI_TYPE_L;
- }
- /* Handle the two rules that effect pp->prev for the last element */
+ last_strong = 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 = 1;
+ /* Resolving dependency of loops for rules W4 and W5 with W7,
+ W7 may change an EN to L but it sets the prev_type_org if needed,
+ so W4 and W5 in next turn can still do their works. */
+ prev_type_org = FRIBIDI_TYPE_ON;
+
+ for (pp = type_rl_list->next; pp->next; pp = pp->next)
+ {
+ gint prev_type, this_type, next_type;
- if (RL_TYPE (pp->prev) == FRIBIDI_TYPE_AL) /* W3 */
- RL_TYPE(pp->prev) = FRIBIDI_TYPE_R;
- if (RL_TYPE (pp->prev) == FRIBIDI_TYPE_EN /* W7 */
- && last_strong == FRIBIDI_TYPE_L)
- RL_TYPE(pp->prev) = FRIBIDI_TYPE_L;
-
+ prev_type = PREV_TYPE_OR_SOR (pp);
+ this_type = RL_TYPE (pp);
+ next_type = NEXT_TYPE_OR_EOR (pp);
- compact_list(type_rl_list);
-
- /* 5. Resolving Neutral Types */
+ if (FRIBIDI_IS_STRONG (prev_type))
+ last_strong = prev_type;
- /* We can now collapse all separators and other neutral types to
- plain neutrals */
- for (pp = type_rl_list->next; pp->next; pp = pp->next)
+ /* W3: Change ALs to R. */
+ if (this_type == FRIBIDI_TYPE_AL)
+ {
+ RL_TYPE (pp) = FRIBIDI_TYPE_RTL;
+ w4 = 1;
+ prev_type_org = FRIBIDI_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 && FRIBIDI_IS_ES_OR_CS (this_type)
+ && FRIBIDI_IS_NUMBER (prev_type_org) && prev_type_org == next_type
+ && (prev_type_org == FRIBIDI_TYPE_EN
+ || this_type == FRIBIDI_TYPE_CS))
+ {
+ RL_TYPE (pp) = prev_type;
+ this_type = RL_TYPE (pp);
+ }
+ w4 = 1;
+
+ /* W5. A sequence of European terminators adjacent to European
+ numbers changes to All European numbers. */
+ if (this_type == FRIBIDI_TYPE_ET
+ && (prev_type_org == FRIBIDI_TYPE_EN
+ || next_type == FRIBIDI_TYPE_EN))
+ {
+ RL_TYPE (pp) = FRIBIDI_TYPE_EN;
+ w4 = 0;
+ this_type = RL_TYPE (pp);
+ }
+
+ /* W6. Otherwise change separators and terminators to other neutral. */
+ if (FRIBIDI_IS_NUMBER_SEPARATOR_OR_TERMINATOR (this_type))
+ RL_TYPE (pp) = FRIBIDI_TYPE_ON;
+
+ /* W7. Change european numbers to L. */
+ if (this_type == FRIBIDI_TYPE_EN && last_strong == FRIBIDI_TYPE_LTR)
+ {
+ RL_TYPE (pp) = FRIBIDI_TYPE_LTR;
+ prev_type_org = (RL_LEVEL (pp) == RL_LEVEL (pp->next) ?
+ FRIBIDI_TYPE_EN : FRIBIDI_TYPE_ON);
+ }
+ else
+ prev_type_org = PREV_TYPE_OR_SOR (pp->next);
+ }
+ }
+
+ compact_neutrals (type_rl_list);
+
+#ifdef DEBUG
+ if (fribidi_debug)
{
- int this_type = RL_TYPE(pp);
-
- if ( this_type == FRIBIDI_TYPE_WS
- || this_type == FRIBIDI_TYPE_ON
- || this_type == FRIBIDI_TYPE_ES
- || this_type == FRIBIDI_TYPE_ET
- || this_type == FRIBIDI_TYPE_CS
- || this_type == FRIBIDI_TYPE_BN)
- RL_TYPE(pp) = FRIBIDI_TYPE_N;
+ print_resolved_levels (type_rl_list);
+ print_resolved_types (type_rl_list);
}
-
- compact_list(type_rl_list);
-
- for (pp = type_rl_list->next; pp->next; pp = pp->next)
- {
- int prev_type = RL_TYPE(pp->prev);
- int this_type = RL_TYPE(pp);
- int next_type = RL_TYPE(pp->next);
+#endif
- if (this_type == FRIBIDI_TYPE_N) /* optimization! */
- {
- /* "European and arabic numbers are treated
- as though they were R" */
+ /* 5. Resolving Neutral Types */
+ DBG ("Resolving neutral types.\n");
+ {
+ gint prev_type, next_type;
+
+ /* N1. and N2.
+ For each neutral, resolve it. */
+ for (pp = type_rl_list->next; pp->next; pp = pp->next)
+ {
+ gint prev_type, this_type, next_type;
- if (prev_type == FRIBIDI_TYPE_EN || prev_type == FRIBIDI_TYPE_AN)
- prev_type = FRIBIDI_TYPE_R;
+ /* "European and arabic numbers are treated as though they were R"
+ FRIBIDI_CHANGE_NUMBER_TO_RTL does this. */
+ this_type = FRIBIDI_CHANGE_NUMBER_TO_RTL (RL_TYPE (pp));
+ prev_type = FRIBIDI_CHANGE_NUMBER_TO_RTL (PREV_TYPE_OR_SOR (pp));
+ next_type = FRIBIDI_CHANGE_NUMBER_TO_RTL (NEXT_TYPE_OR_EOR (pp));
- if (next_type == FRIBIDI_TYPE_EN || next_type == FRIBIDI_TYPE_AN)
- next_type = FRIBIDI_TYPE_R;
+ if (FRIBIDI_IS_NEUTRAL (this_type))
+ RL_TYPE (pp) = (prev_type == next_type) ?
+ /* N1. */ prev_type :
+ /* N2. */ FRIBIDI_EMBEDDING_DIRECTION (pp);
+ }
+ }
- /* N1. */
- TYPE_RULE_C(R,N,R, R);
- TYPE_RULE_C(L,N,L, L);
+ compact_list (type_rl_list);
- /* N2. Any remaining neutrals takes the embedding direction */
- if (RL_TYPE(pp) == FRIBIDI_TYPE_N)
- RL_TYPE(pp) = FRIBIDI_TYPE_E;
- }
+#ifdef DEBUG
+ if (fribidi_debug)
+ {
+ print_resolved_levels (type_rl_list);
+ print_resolved_types (type_rl_list);
}
+#endif
- compact_list(type_rl_list);
-
- /* 6. Resolving Implicit levels */
+ /* 6. Resolving implicit levels */
+ DBG ("Resolving implicit levels.\n");
{
- int level = base_level;
max_level = base_level;
-
+
for (pp = type_rl_list->next; pp->next; pp = pp->next)
{
- int this_type = RL_TYPE(pp);
+ gint this_type, level;
+
+ this_type = RL_TYPE (pp);
+ level = RL_LEVEL (pp);
+
+ /* I1. Even */
+ /* I2. Odd */
+ if (FRIBIDI_IS_NUMBER (this_type))
+ RL_LEVEL (pp) = (RL_LEVEL (pp) + 2) & ~1;
+ else
+ RL_LEVEL (pp) = (RL_LEVEL (pp) ^ FRIBIDI_DIR_TO_LEVEL (this_type)) +
+ (RL_LEVEL (pp) & 1);
+
+ if (RL_LEVEL (pp) > max_level)
+ max_level = RL_LEVEL (pp);
+ }
+ }
+
+ compact_list (type_rl_list);
+
+#ifdef DEBUG
+ if (fribidi_debug)
+ {
+ print_bidi_string (str);
+ print_resolved_levels (type_rl_list);
+ print_resolved_types (type_rl_list);
+ }
+#endif
+
+/* Reinsert the explicit codes & bn's that already removed, from the
+ explicits_list to type_rl_list. */
+ DBG ("Reinserting explicit codes.\n");
+ {
+ TypeLink *p;
+
+ override_list (type_rl_list, explicits_list);
+ p = type_rl_list->next;
+ if (p->level < 0)
+ p->level = base_level;
+ for (; p->next; p = p->next)
+ if (p->level < 0)
+ p->level = p->prev->level;
+ }
- /* This code should be expanded to handle explicit directions! */
+#ifdef DEBUG
+ if (fribidi_debug)
+ {
+ print_types_re (type_rl_list);
+ print_resolved_levels (type_rl_list);
+ print_resolved_types (type_rl_list);
+ }
+#endif
- /* Even */
- if (level % 2 == 0)
+ DBG ("Reset the embedding levels.\n");
+ {
+ gint j, k, state, pos;
+ TypeLink *p, *q, *list, *list_end;
+
+ /* L1. Reset the embedding levels of some chars. */
+ init_list (&list, &list_end);
+ q = list_end;
+ state = 1;
+ pos = len - 1;
+ for (j = len - 1; j >= 0; j--)
+ {
+ k = _pango_fribidi_get_type (str[j]);
+ if (!state && FRIBIDI_IS_SEPARATOR (k))
{
- if (this_type == FRIBIDI_TYPE_R)
- RL_LEVEL(pp) = level + 1;
- else if (this_type == FRIBIDI_TYPE_AN)
- RL_LEVEL(pp) = level + 2;
- else if (RL_TYPE(pp->prev) != FRIBIDI_TYPE_L && this_type == FRIBIDI_TYPE_EN)
- RL_LEVEL(pp) = level + 2;
- else
- RL_LEVEL(pp) = level;
+ state = 1;
+ pos = j;
}
- /* Odd */
else
+ if (state
+ && !(j && FRIBIDI_IS_EXPLICIT_OR_SEPARATOR_OR_BN_OR_WS (k)))
{
- if ( this_type == FRIBIDI_TYPE_L
- || this_type == FRIBIDI_TYPE_AN
- || this_type == FRIBIDI_TYPE_EN)
- RL_LEVEL(pp) = level+1;
- else
- RL_LEVEL(pp) = level;
+ /* if state is on at the very first of string, do this too. */
+ if (!j)
+ j--;
+ state = 0;
+ p = new_type_link ();
+ p->prev = p->next = 0;
+ p->pos = j + 1;
+ p->len = pos - j;
+ p->type = base_dir;
+ p->level = base_level;
+ move_element_before (p, q);
+ q = p;
}
-
- if (RL_LEVEL(pp) > max_level)
- max_level = RL_LEVEL(pp);
}
+ override_list (type_rl_list, list);
}
-
- compact_list(type_rl_list);
+
+#ifdef DEBUG
+ if (fribidi_debug)
+ {
+ print_types_re (type_rl_list);
+ print_resolved_levels (type_rl_list);
+ print_resolved_types (type_rl_list);
+ }
+#endif
*ptype_rl_list = type_rl_list;
*pmax_level = max_level;
@@ -467,6 +1028,45 @@ fribidi_analyse_string(/* input */
}
/*======================================================================
+// Frees up the rl_list, must be called after each call to
+// fribidi_analyse_string(), after the list is not needed anymore.
+//----------------------------------------------------------------------*/
+void
+free_rl_list (TypeLink * type_rl_list)
+{
+
+ TypeLink *p, *pp;
+ if (!type_rl_list)
+ return;
+#ifdef USE_SIMPLE_MALLOC
+ for (pp = type_rl_list; pp; pp = pp->next)
+ {
+ p = pp->next;
+ g_free (pp);
+ };
+#else
+ for (pp = type_rl_list->next; pp->next; pp = pp->next)
+ /*Nothing */ ;
+ pp->next = free_type_links;
+ free_type_links = type_rl_list;
+ type_rl_list = NULL;
+#endif
+}
+
+static gboolean mirroring = TRUE;
+
+gboolean fribidi_mirroring_status (void)
+{
+ return mirroring;
+}
+
+void
+fribidi_set_mirroring (gboolean mirror)
+{
+ mirroring = mirror;
+}
+
+/*======================================================================
// Here starts the exposed front end functions.
//----------------------------------------------------------------------*/
@@ -474,41 +1074,39 @@ fribidi_analyse_string(/* input */
// fribidi_embedding_levels() is used in order to just get the
// embedding levels.
//----------------------------------------------------------------------*/
-void
-pango_log2vis_get_embedding_levels(
- /* input */
- gunichar *str,
- int len,
- PangoDirection *pbase_dir,
- /* output */
- guint8 *embedding_level_list
- )
+void
+pango_log2vis_get_embedding_levels (
+ /* input */
+ gunichar * str,
+ int len,
+ PangoDirection *pbase_dir,
+ /* output */
+ guint8 * embedding_level_list)
{
TypeLink *type_rl_list, *pp;
- int max_level;
+ gint max_level;
FriBidiCharType fribidi_base_dir;
-
+
fribidi_base_dir = (*pbase_dir == PANGO_DIRECTION_LTR) ? FRIBIDI_TYPE_L : FRIBIDI_TYPE_R;
-
- fribidi_analyse_string(str, len, &fribidi_base_dir,
- /* output */
- &type_rl_list,
- &max_level);
+
+ if (len == 0)
+ return;
+
+ fribidi_analyse_string (str, len, &fribidi_base_dir,
+ /* output */
+ &type_rl_list, &max_level);
for (pp = type_rl_list->next; pp->next; pp = pp->next)
{
- int i;
- int pos = RL_POS(pp);
- int len = RL_LEN(pp);
- int level = RL_LEVEL(pp);
- for (i=0; i<len; i++)
+ gint i;
+ gint pos = RL_POS (pp);
+ gint len = RL_LEN (pp);
+ gint level = RL_LEVEL (pp);
+ for (i = 0; i < len; i++)
embedding_level_list[pos + i] = level;
}
-
- /* Free up the rl_list */
- pp->next = free_type_links;
- free_type_links = type_rl_list;
-
+
+ free_rl_list (type_rl_list);
+
*pbase_dir = (fribidi_base_dir == FRIBIDI_TYPE_L) ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
}
-