Imported Upstream version 1.0.5
[platform/upstream/fribidi.git] / lib / fribidi-joining.c
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
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
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  */