Git init
[external/pango1.0.git] / modules / syriac / syriac-ot.c
1 /* Pango
2  * syriac-ot.h: Determine what OpenType features to apply to characters based
3  *              on the rules for Syriac from the OpenType standard.
4  *
5  * Copyright (C) 2004 Emil Soleyman-Zomalan
6  * Author: Emil Soleyman-Zomalan <emil@soleyman.com>
7  *
8  * This file is based on the Arabic shaping code from FreeType 1 tree; original
9  * copyright notice:
10  *
11  *  The FreeType project -- a free and portable quality TrueType renderer.
12  *
13  *  Copyright 1996-2000 by
14  *  D. Turner, R.Wilhelm, and W. Lemberg
15  *
16  * The code, like the FreeType code it is derived from is dual-licensed
17  * under the GNU General Public License and the FreeType license. See
18  * pango/opentype/COPYING for full details of this licensing scheme.
19  */
20 #include "config.h"
21 #include "syriac-ot.h"
22
23 /*     Here a table of the joining classes for characters in the range
24  *     U+0700 - U+074F.
25  *
26  *     The following character also has a joining class:
27  *
28  *     U+200C  ZERO WIDTH NON-JOINER  -> causing
29  *
30  *     All other characters are given the joining class `none'.
31  */
32 static const JoiningClass syriac[] =
33 {
34   /* U+0700 */
35   none, none, none, none,
36   none, none, none, none,
37   none, none, none, none,
38   none, none, none, transparent,
39
40   /* U+0710 */
41   right, none, dual, dual,
42   dual, right, right, right,
43   right, right, dual, dual,
44   dual, dual, right, dual,
45
46   /* U+0720 */
47   dual, dual, dual, dual,
48   dual, dual, dual, dual,
49   right, dual, right, dual,
50   right, none, none, none,
51
52   /* U+0730 */
53   transparent, transparent, transparent, transparent,
54   transparent, transparent, transparent, transparent,
55   transparent, transparent, transparent, transparent,
56   transparent, transparent, transparent, transparent,
57
58   /* U+0740 */
59   transparent, transparent, transparent, transparent,
60   transparent, transparent, transparent, transparent,
61   transparent, transparent, transparent, none,
62   none, right, dual, dual
63 };
64
65 /* `direction' can be -1, 0, or 1 to indicate the last non-transparent
66  * glyph, the current glyph, and the next non-transparent glyph,
67  * respectively.
68  */
69 static JoiningClass
70 Get_Joining_Class (gunichar*   string,
71                    int         pos,
72                    int         length,
73                    int         direction)
74 {
75   JoiningClass  j;
76
77   while (1)
78     {
79       if (pos == 0 && direction < 0)
80         return none;
81
82       pos += direction;
83
84       if (pos >= length)
85         return none;
86
87       if (string[pos] < 0x0700 ||
88           string[pos] >= 0x074F)
89         {
90           if (string[pos] == 0x200C)
91             return causing;
92           else
93             return none;
94         }
95       else
96         j =  syriac[string[pos] - 0x0700];
97
98       if (!direction || j != transparent)
99         return j;
100     }
101 }
102
103
104 /* The rules here are roughly based on the Arabic rules from the Unicode
105  * 2.0 standard (which differ from the Unicode-4.0 rules), augmented
106  * with the Syriac rules from the Unicode-4.0 standard. The numbers
107  * R1...R11  below do not correspond to either the Arabic or the Syriac
108  * rule numbering from the Unicode standard.
109  *
110  * Characters are here specified as appearing in the byte stream, i.e.
111  * *not* in visual order. Joining classes are given in angle brackets,
112  * glyph forms in square brackets.  Glyphs affected by a specific rule are
113  * enclosed with vertical bars.
114  *
115  *
116  * Glyphs: 0x0715 (Dalath), 0x0716 (Dalath Rish), 0x072A (Rish),
117  *         0x0722 (Nun), 0x071F (Kaph)
118  *
119  *
120  *   R1: <anything1> <transparent> <anything2>
121  *
122  *       apply joining rules for
123  *       <anything1> <anything2> -> [shape1] [shape2]
124  *       -> [shape1] [isolated] [shape2]
125  *
126  *
127  *   R2: <causing|right> <0x0722|0x071F> <!(causing|right|dual)>
128  *       -> [isolated]
129  *
130  *       The Nun and Kaph characters each have 3 different glyphs
131  *       with two of those glyphs coming at the final position.
132  *       However, one of those final glyphs should really be of the
133  *       isolated glyph form where the preceding character cannot be
134  *       joined to and there is no next character.
135  *
136  *       This rule exists to combine similar exception for both
137  *       characters without increasing the complexity in the other
138  *       rules.
139  *
140  *
141  *   R3: <causing|right|dual> && <!(0x0715|0x0716|0x072A)> |<alaph>|
142  *
143  *       -> [final2]
144  *
145  *       If the preceding glyph cannot be joined to the current
146  *       glyph and the preceding character is not a Dalath, Rish,
147  *       or Dotless Dalath Rish, then the Alaph takes this contextual
148  *       position.
149  *
150  *       The [final2] joining rule is placed ahead of the [final] to
151  *       give it greater precedence when choosing the correct glyph.
152  *       If it comes after the [final] rule, the incorrect glyph is
153  *       inserted into position.
154  *
155  *
156  *   R4: <0x0715|0x0715|0x072A> |<alaph>|
157  *
158  *       -> [final3]
159  *
160  *       If the previous glyph is a Dalath, Rish, or Dotless Dalath
161  *       Rish, then the Alaph takes this contextual position.
162  *
163  *       The [final3] joining rule is placed ahead of the [final] to
164  *       give it greater precedence when choosing the correct glyph.
165  *       If it comes after the [final] rule, the incorrect glyph is
166  *       inserted into position.
167  *
168  *
169  *   R5: <causing|right|dual> |<right>|
170  *
171  *       -> [final]
172  *
173  *
174  *   R6: <causing|right|dual> |<dual>| <!(causing|right|dual)>
175  *
176  *       -> [final]
177  *
178  *
179  *   R7: <causing|left|dual> |<dual>| <causing|right|dual>
180  *
181  *       -> [medial]
182  *
183  *
184  *   R8: <causing|right> |<alaph>| <causing|right|dual>
185  *
186  *       -> [medial2]
187  *
188  *       If the Alaph glyph falls in the middle of a Syriac word and
189  *       the preceding character cannot be joined to, then the Alaph
190  *       takes this contextual position.
191  *
192  *
193  *   R9: |<left>| <causing|right|dual>
194  *
195  *       -> [initial]
196  *
197  *
198  *   R10: <!(causing|left|dual)> |<dual>| <causing|right|dual>
199  *
200  *       -> [initial]
201  *
202  *
203  *   R11: <anything> -> [isolated]
204  *
205  *        This joining rule is placed at the end of these features
206  *        because when it is placed at the beginning of all of them
207  *        it tends to break the cursive nature of Syriac writing --
208  *        it inserts the isolated glyph of each character into that
209  *        position with no joining occurring all throughout a text
210  *        document.
211  */
212
213 FT_Error
214 syriac_assign_properties (gunichar    *string,
215                           gulong      *properties,
216                           int          length)
217 {
218   JoiningClass previous, current, next;
219   int i;
220
221   if (!string || !properties || length == 0)
222     return FT_Err_Invalid_Argument;
223
224   for (i = 0; i < length; i++)
225     {
226       previous = Get_Joining_Class (string, i, length, -1);
227       current  = Get_Joining_Class (string, i, length,  0);
228       next     = Get_Joining_Class (string, i, length,  1);
229
230       /* R1 */
231
232       if (current == transparent)
233         {
234           properties[i] |= isolated_p;
235           continue;
236         }
237
238       /* R2 */
239
240       if (string[i] == 0x0722 ||
241           string[i] == 0x071F)
242         if (previous == causing ||
243             previous == right)
244           if (!(next == causing ||
245                 next == right ||
246                 next == dual))
247           {
248             properties[i] |= isolated_p;
249             continue;
250           }
251
252       /* R3 */
253
254       if (string[i] == 0x0710)
255         if (previous == causing ||
256             previous == right)
257           if (!(string[i - 1] == 0x0715 ||
258                 string[i - 1] == 0x0716 ||
259                 string[i - 1] == 0x072A))
260           {
261             properties[i] |= final2_p;
262             continue;
263           }
264
265       /* R4 */
266
267       if (string[i] == 0x0710)
268         if (previous == causing ||
269             previous == right)
270          if (string[i - 1] == 0x0715 ||
271               string[i - 1] == 0x0716 ||
272               string[i - 1] == 0x072A)
273           {
274             properties[i] |= final3_p;
275             continue;
276           }
277
278       /* R5 */
279
280       if (previous == causing ||
281           previous == right   ||
282           previous == dual)
283         if (current == right)
284             {
285               properties[i] |= final_p;
286               continue;
287             }
288
289       /* R6 */
290
291       if (previous == causing ||
292           previous == right   ||
293           previous == dual)
294         if (current == dual)
295           if (!(next == causing ||
296                     next == right   ||
297                     next == dual   ))
298               {
299                 properties[i] |= final_p;
300                 continue;
301               }
302
303       /* R7 */
304
305       if (previous == causing ||
306           previous == left    ||
307           previous == dual)
308         if (current == dual)
309           if (next == causing ||
310               next == right   ||
311               next == dual   )
312               {
313                 properties[i] |= medial_p;
314                 continue;
315               }
316
317       /* R8 */
318
319       if (string[i] == 0x0710)
320         if (previous == causing ||
321             previous == right)
322             if (next == causing ||
323                 next == right ||
324                 next == dual)
325             {
326               properties[i] |= medial2_p;
327               continue;
328             }
329
330       /* R9 */
331
332       if (current == left)
333         if (next == causing ||
334             next == right   ||
335             next == dual)
336             {
337               properties[i] |= initial_p;
338               continue;
339             }
340
341       /* R10 */
342
343       if (!(previous == causing ||
344             previous == left ||
345             previous == dual   ))
346         if (current == dual)
347           if (next == causing ||
348               next == right   ||
349               next == dual)
350               {
351                 properties[i] |= initial_p;
352                 continue;
353               }
354
355       /* R11 */
356
357       properties[i] |= isolated_p;
358     }
359
360   return FT_Err_Ok;
361 }