1 /* FriBidi
2  * fribidi-joining.h - Arabic joining algorithm
3  *
4  * Authors:
5  *   Behdad Esfahbod, 2004
6  *
7  * Copyright (C) 2004 Sharif FarsiWeb, Inc
8  * Copyright (C) 2004 Behdad Esfahbod
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this library, in a file named COPYING; if not, write to the
22  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA
24  *
25  * For licensing issues, contact <fribidi.license@gmail.com>.
26  */
27 
28 #include "common.h"
29 
30 #include <fribidi-joining.h>
31 
32 #include "bidi-types.h"
33 #include "joining-types.h"
34 
35 #ifdef DEBUG
36 /*======================================================================
37  *  For debugging, define some functions for printing joining types and
38  *  properties.
39  *----------------------------------------------------------------------*/
40 
41 static void
print_joining_types(const FriBidiLevel * embedding_levels,const FriBidiStrIndex len,const FriBidiJoiningType * jtypes)42 print_joining_types (
43   /* input */
44   const FriBidiLevel *embedding_levels,
45   const FriBidiStrIndex len,
46   const FriBidiJoiningType *jtypes
47 )
48 {
49   register FriBidiStrIndex i;
50 
51   fribidi_assert (jtypes);
52 
53   MSG ("  Join. types: ");
54   for (i = 0; i < len; i++)
55     MSG2 ("%c", fribidi_char_from_joining_type (jtypes[i],
56 						!FRIBIDI_LEVEL_IS_RTL
57 						(embedding_levels[i])));
58   MSG ("\n");
59 }
60 #endif /* DEBUG */
61 
62 #define FRIBIDI_CONSISTENT_LEVEL(i)	\
63 	(FRIBIDI_IS_EXPLICIT_OR_BN (bidi_types[(i)])	\
64 	 ? FRIBIDI_SENTINEL	\
65 	 : embedding_levels[(i)])
66 
67 #define FRIBIDI_LEVELS_MATCH(i, j)	\
68 	((i) == (j) || (i) == FRIBIDI_SENTINEL || (j) == FRIBIDI_SENTINEL)
69 
70 FRIBIDI_ENTRY void
fribidi_join_arabic(const FriBidiCharType * bidi_types,const FriBidiStrIndex len,const FriBidiLevel * embedding_levels,FriBidiArabicProp * ar_props)71 fribidi_join_arabic (
72   /* input */
73   const FriBidiCharType *bidi_types,
74   const FriBidiStrIndex len,
75   const FriBidiLevel *embedding_levels,
76   /* input and output */
77   FriBidiArabicProp *ar_props
78 )
79 {
80   if UNLIKELY
81     (len == 0) return;
82 
83   DBG ("in fribidi_join_arabic");
84 
85   fribidi_assert (bidi_types);
86   fribidi_assert (embedding_levels);
87   fribidi_assert (ar_props);
88 
89 # if DEBUG
90   if UNLIKELY
91     (fribidi_debug_status ())
92     {
93       print_joining_types (embedding_levels, len, ar_props);
94     }
95 # endif	/* DEBUG */
96 
97   /* The joining algorithm turned out very very dirty :(.  That's what happens
98    * when you follow the standard which has never been implemented closely
99    * before.
100    */
101 
102   /* 8.2 Arabic - Cursive Joining */
103   DBG ("Arabic cursive joining");
104   {
105     /* The following do not need to be initialized as long as joins is
106      * initialized to false.  We just do to turn off compiler warnings. */
107     register FriBidiStrIndex saved = 0;
108     register FriBidiLevel saved_level = FRIBIDI_SENTINEL;
109     register fribidi_boolean saved_shapes = false;
110     register FriBidiArabicProp saved_joins_following_mask = 0;
111 
112     register fribidi_boolean joins = false;
113     register FriBidiStrIndex i;
114 
115     for (i = 0; i < len; i++)
116       if (!FRIBIDI_IS_JOINING_TYPE_G (ar_props[i]))
117 	{
118 	  register fribidi_boolean disjoin = false;
119 	  register fribidi_boolean shapes = FRIBIDI_ARAB_SHAPES (ar_props[i]);
120 	  register FriBidiLevel level = FRIBIDI_CONSISTENT_LEVEL (i);
121 
122 	  if (joins && !FRIBIDI_LEVELS_MATCH (saved_level, level))
123 	    {
124 	      disjoin = true;
125 	      joins = false;
126 	    }
127 
128 	  if (!FRIBIDI_IS_JOIN_SKIPPED (ar_props[i]))
129 	    {
130 	      register const FriBidiArabicProp joins_preceding_mask =
131 		FRIBIDI_JOINS_PRECEDING_MASK (level);
132 
133 	      if (!joins)
134 		{
135 		  if (shapes)
136 		    FRIBIDI_UNSET_BITS (ar_props[i], joins_preceding_mask);
137 		}
138 	      else if (!FRIBIDI_TEST_BITS (ar_props[i], joins_preceding_mask))
139 	        {
140 		  disjoin = true;
141 		}
142 	      else
143 	        {
144 		  register FriBidiStrIndex j;
145 		  /* This is a FriBidi extension:  we set joining properties
146 		   * for skipped characters in between, so we can put NSMs on tatweel
147 		   * later if we want.  Useful on console for example.
148 		   */
149 		  for (j = saved + 1; j < i; j++)
150 		    FRIBIDI_SET_BITS (ar_props[j], joins_preceding_mask | saved_joins_following_mask);
151 		}
152 	    }
153 
154 	  if (disjoin && saved_shapes)
155 	    FRIBIDI_UNSET_BITS (ar_props[saved], saved_joins_following_mask);
156 
157 	  if (!FRIBIDI_IS_JOIN_SKIPPED (ar_props[i]))
158 	    {
159 	      saved = i;
160 	      saved_level = level;
161 	      saved_shapes = shapes;
162 	      saved_joins_following_mask =
163 		FRIBIDI_JOINS_FOLLOWING_MASK (level);
164 	      joins =
165 		FRIBIDI_TEST_BITS (ar_props[i], saved_joins_following_mask);
166 	    }
167 	}
168     if ((joins) && saved_shapes)
169       FRIBIDI_UNSET_BITS (ar_props[saved], saved_joins_following_mask);
170 
171   }
172 
173 # if DEBUG
174   if UNLIKELY
175     (fribidi_debug_status ())
176     {
177       print_joining_types (embedding_levels, len, ar_props);
178     }
179 # endif	/* DEBUG */
180 
181   DBG ("leaving fribidi_join_arabic");
182 }
183 
184 /* Editor directions:
185  * vim:textwidth=78:tabstop=8:shiftwidth=2:autoindent:cindent
186  */
187