Git init
[external/pango1.0.git] / modules / thai / thai-shaper.c
1 /* Pango
2  * thai-shaper.c:
3  *
4  * Copyright (C) 1999 Red Hat Software
5  * Author: Owen Taylor <otaylor@redhat.com>
6  *
7  * Copyright (C) 2005 Theppitak Karoonboonyanan
8  * Copyright (C) 2002 Software and Language Engineering Laboratory, NECTEC
9  * Author: Theppitak Karoonboonyanan <thep@links.nectec.or.th>
10  *
11  * Copyright (c) 1996-2000 by Sun Microsystems, Inc.
12  * Author: Chookij Vanatham <Chookij.Vanatham@Eng.Sun.COM>
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Library General Public
16  * License as published by the Free Software Foundation; either
17  * version 2 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Library General Public License for more details.
23  *
24  * You should have received a copy of the GNU Library General Public
25  * License along with this library; if not, write to the
26  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27  * Boston, MA 02111-1307, USA.
28  */
29 #include "config.h"
30 #include <string.h>
31
32 #include <glib.h>
33 #include "pango-engine.h"
34 #include "thai-charprop.h"
35 #include "thai-shaper.h"
36
37 #define MAX_CLUSTER_CHRS        256
38 #define MAX_GLYPHS              256
39
40
41 typedef struct {
42   guchar Start_TONE_AD, Start_AV, Start_BV_BD, Start_TailCutCons;
43
44   guchar ShiftDown_TONE_AD[8];
45   guchar ShiftDownLeft_TONE_AD[8];
46   guchar ShiftLeft_TONE_AD[8];
47   guchar ShiftLeft_AV[7];
48   guchar ShiftDown_BV_BD[3];
49   guchar TailCutCons[4];
50
51   guchar AmComp[2];  /* Sara Am components */
52 } ThaiShapeTable;
53
54 #define shiftdown_tone_ad(c,tbl) \
55         ((tbl)->ShiftDown_TONE_AD[(c)-(tbl)->Start_TONE_AD])
56 #define shiftdownleft_tone_ad(c,tbl) \
57         ((tbl)->ShiftDownLeft_TONE_AD[(c)-(tbl)->Start_TONE_AD])
58 #define shiftleft_tone_ad(c,tbl) \
59         ((tbl)->ShiftLeft_TONE_AD[(c)-(tbl)->Start_TONE_AD])
60 #define shiftleft_av(c,tbl) \
61         ((tbl)->ShiftLeft_AV[(c)-(tbl)->Start_AV])
62 #define shiftdown_bv_bd(c,tbl) \
63         ((tbl)->ShiftDown_BV_BD[(c)-(tbl)->Start_BV_BD])
64 #define tailcutcons(c,tbl) \
65         ((tbl)->TailCutCons[(c)-(tbl)->Start_TailCutCons])
66
67 /* No adjusted vowel/tonemark glyphs (tis620-0)
68  */
69 static const ThaiShapeTable tis620_0_shape_table = {
70     0xE7, 0xD1, 0xD8, 0xAD,
71     { 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE },
72     { 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE },
73     { 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE },
74     { 0xD1, 0x00, 0x00, 0xD4, 0xD5, 0xD6, 0xD7 },
75     { 0xD8, 0xD9, 0xDA },
76     { 0xAD, 0x00, 0x00, 0xB0 },
77     { 0xED, 0xD2 }
78 };
79
80 /* Macintosh
81  */
82 static const ThaiShapeTable Mac_shape_table = {
83   0xE7, 0xD1, 0xD8, 0xAD,
84   { 0xE7, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0xED, 0xEE },
85   { 0x93, 0x83, 0x84, 0x85, 0x86, 0x87, 0x8F, 0xEE },
86   { 0x93, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x8F, 0xEE },
87   { 0x92, 0x00, 0x00, 0x94, 0x95, 0x96, 0x97 },
88   { 0xFC, 0xFD, 0xFE },
89   { 0x90, 0x00, 0x00, 0x80 },
90   { 0xED, 0xD2 }
91 };
92
93 /* Microsoft Window
94  */
95 static const ThaiShapeTable Win_shape_table = {
96     0xE7, 0xD1, 0xD8, 0xAD,
97     { 0xE7, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0xED, 0xEE },
98     { 0x9A, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x99, 0xEE },
99     { 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0x99, 0xEE },
100     { 0x98, 0x00, 0x00, 0x81, 0x82, 0x83, 0x84 },
101     { 0xFC, 0xFD, 0xFE },
102     { 0x90, 0x00, 0x00, 0x80 },
103     { 0xED, 0xD2 }
104 };
105
106 static const ThaiShapeTable Lao_shape_table = {
107   0x67, 0x51, 0x58, 0x2D,
108   { 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E },
109   { 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E },
110   { 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E },
111   { 0x51, 0x00, 0x00, 0x54, 0x55, 0x56, 0x57 },
112   { 0x58, 0x59, 0x00 },
113   { 0x2D, 0x00, 0x00, 0x30 },
114   { 0x6D, 0x52 }
115 };
116
117 static void
118 add_glyph (ThaiFontInfo     *font_info,
119            PangoGlyphString *glyphs,
120            gint              cluster_start,
121            PangoGlyph        glyph,
122            gboolean          combining)
123 {
124   PangoRectangle ink_rect, logical_rect;
125   gint index = glyphs->num_glyphs;
126
127   pango_glyph_string_set_size (glyphs, index + 1);
128
129   glyphs->glyphs[index].glyph = glyph;
130   glyphs->glyphs[index].attr.is_cluster_start = combining ? 0 : 1;
131
132   glyphs->log_clusters[index] = cluster_start;
133
134   pango_font_get_glyph_extents (font_info->font,
135                                 glyphs->glyphs[index].glyph, &ink_rect, &logical_rect);
136
137   if (combining || logical_rect.width > 0)
138     {
139       glyphs->glyphs[index].geometry.x_offset = 0;
140       glyphs->glyphs[index].geometry.width = logical_rect.width;
141     }
142   else
143     {
144       glyphs->glyphs[index].geometry.x_offset = ink_rect.width;
145       glyphs->glyphs[index].geometry.width = ink_rect.width;
146     }
147   glyphs->glyphs[index].geometry.y_offset = 0;
148 }
149
150 static PangoGlyph
151 get_null_base_glyph (ThaiFontInfo *font_info)
152 {
153   return thai_get_glyph_uni (font_info, 0x25cc);
154 }
155
156 static gint
157 get_adjusted_glyphs_list (ThaiFontInfo *font_info,
158                           gunichar *cluster,
159                           gint num_chrs,
160                           PangoGlyph *glyph_lists,
161                           const ThaiShapeTable *shaping_table)
162 {
163   switch (num_chrs)
164     {
165       case 1:
166         if (is_char_type (cluster[0],
167                           BelowVowel|BelowDiac|AboveVowel|AboveDiac|Tone|SaraAm))
168           {
169             gint n;
170             glyph_lists[0] = get_null_base_glyph (font_info);
171             n = glyph_lists[0] ? 1 : 0;
172             glyph_lists[n++] =
173                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
174             return n;
175           }
176         else
177           {
178             glyph_lists[0] =
179                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
180             return 1;
181           }
182         break;
183
184       case 2:
185         if (is_char_type (cluster[0], NoTailCons|BotTailCons|SpltTailCons) &&
186             is_char_type (cluster[1], SaraAm))
187           {
188             glyph_lists[0] =
189                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
190             glyph_lists[1] = thai_make_glyph_tis (font_info, shaping_table->AmComp[0]);
191             glyph_lists[2] = thai_make_glyph_tis (font_info, shaping_table->AmComp[1]);
192             return 3;
193           }
194         else if (is_char_type (cluster[0], UpTailCons) &&
195                  is_char_type (cluster[1], SaraAm))
196           {
197             glyph_lists[0] =
198                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
199             glyph_lists[1] = thai_make_glyph_tis (font_info,
200                                         shiftleft_tone_ad (shaping_table->AmComp[0], shaping_table));
201             glyph_lists[2] = thai_make_glyph_tis (font_info, shaping_table->AmComp[1]);
202             return 3;
203           }
204         else if (is_char_type (cluster[0], NoTailCons|BotTailCons|SpltTailCons) &&
205                  is_char_type (cluster[1], AboveVowel))
206           {
207             glyph_lists[0] =
208                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
209             glyph_lists[1] =
210                 thai_make_glyph_tis (font_info, ucs2tis (cluster[1]));
211             return 2;
212           }
213         else if (is_char_type (cluster[0], NoTailCons|BotTailCons|SpltTailCons) &&
214                  is_char_type (cluster[1], AboveDiac|Tone))
215           {
216             glyph_lists[0] =
217                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
218             glyph_lists[1] = thai_make_glyph_tis (font_info,
219                         shiftdown_tone_ad (ucs2tis (cluster[1]), shaping_table));
220             return 2;
221           }
222         else if (is_char_type (cluster[0], UpTailCons) &&
223                  is_char_type (cluster[1], AboveVowel))
224           {
225             glyph_lists[0] =
226                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
227             glyph_lists[1] = thai_make_glyph_tis (font_info,
228                         shiftleft_av (ucs2tis (cluster[1]), shaping_table));
229             return 2;
230           }
231         else if (is_char_type (cluster[0], UpTailCons) &&
232                  is_char_type (cluster[1], AboveDiac|Tone))
233           {
234             glyph_lists[0] =
235                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
236             glyph_lists[1] = thai_make_glyph_tis (font_info,
237                         shiftdownleft_tone_ad (ucs2tis (cluster[1]), shaping_table));
238             return 2;
239           }
240         else if (is_char_type (cluster[0], NoTailCons|UpTailCons) &&
241                  is_char_type (cluster[1], BelowVowel|BelowDiac))
242           {
243             glyph_lists[0] =
244                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
245             glyph_lists[1] =
246                 thai_make_glyph_tis (font_info, ucs2tis (cluster[1]));
247             return 2;
248           }
249         else if (is_char_type (cluster[0], BotTailCons) &&
250                  is_char_type (cluster[1], BelowVowel|BelowDiac))
251           {
252             glyph_lists[0] =
253                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
254             glyph_lists[1] =
255                 thai_make_glyph_tis (font_info,
256                         shiftdown_bv_bd (ucs2tis (cluster[1]), shaping_table));
257             return 2;
258           }
259         else if (is_char_type (cluster[0], SpltTailCons) &&
260                  is_char_type (cluster[1], BelowVowel|BelowDiac))
261           {
262             glyph_lists[0] = thai_make_glyph_tis (font_info,
263                                tailcutcons (ucs2tis (cluster[0]), shaping_table));
264             glyph_lists[1] =
265                 thai_make_glyph_tis (font_info, ucs2tis (cluster[1]));
266             return 2;
267           }
268         else
269           {
270             gint n;
271             glyph_lists[0] = get_null_base_glyph (font_info);
272             n = glyph_lists[0] ? 1 : 0;
273             glyph_lists[n++] =
274                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
275             glyph_lists[n++] =
276                 thai_make_glyph_tis (font_info, ucs2tis (cluster[1]));
277             return n;
278           }
279         break;
280
281       case 3:
282         if (is_char_type (cluster[0], NoTailCons|BotTailCons|SpltTailCons) &&
283             is_char_type (cluster[1], Tone) &&
284             is_char_type (cluster[2], SaraAm))
285           {
286             glyph_lists[0] =
287                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
288             glyph_lists[1] = thai_make_glyph_tis (font_info, shaping_table->AmComp[0]);
289             glyph_lists[2] =
290                 thai_make_glyph_tis (font_info, ucs2tis (cluster[1]));
291             glyph_lists[3] = thai_make_glyph_tis (font_info, shaping_table->AmComp[1]);
292             return 4;
293           }
294         else if (is_char_type (cluster[0], UpTailCons) &&
295                  is_char_type (cluster[1], Tone) &&
296                  is_char_type (cluster[2], SaraAm))
297           {
298             glyph_lists[0] =
299                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
300             glyph_lists[1] = thai_make_glyph_tis (font_info,
301                                 shiftleft_tone_ad (shaping_table->AmComp[0], shaping_table));
302             glyph_lists[2] = thai_make_glyph_tis (font_info,
303                                 shiftleft_tone_ad (ucs2tis (cluster[1]), shaping_table));
304             glyph_lists[3] = thai_make_glyph_tis (font_info, shaping_table->AmComp[1]);
305             return 4;
306           }
307         else if (is_char_type (cluster[0], UpTailCons) &&
308                  is_char_type (cluster[1], AboveVowel) &&
309                  is_char_type (cluster[2], AboveDiac|Tone))
310           {
311             glyph_lists[0] =
312                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
313             glyph_lists[1] = thai_make_glyph_tis (font_info,
314                                 shiftleft_av (ucs2tis (cluster[1]), shaping_table));
315             glyph_lists[2] = thai_make_glyph_tis (font_info,
316                                 shiftleft_tone_ad (ucs2tis (cluster[2]), shaping_table));
317             return 3;
318           }
319         else if (is_char_type (cluster[0], UpTailCons) &&
320                  is_char_type (cluster[1], BelowVowel) &&
321                  is_char_type (cluster[2], AboveDiac|Tone))
322           {
323             glyph_lists[0] =
324                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
325             glyph_lists[1] =
326                 thai_make_glyph_tis (font_info, ucs2tis (cluster[1]));
327             glyph_lists[2] = thai_make_glyph_tis (font_info,
328                         shiftdownleft_tone_ad (ucs2tis (cluster[2]), shaping_table));
329             return 3;
330           }
331         else if (is_char_type (cluster[0], NoTailCons) &&
332                  is_char_type (cluster[1], BelowVowel) &&
333                  is_char_type (cluster[2], AboveDiac|Tone))
334           {
335             glyph_lists[0] =
336                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
337             glyph_lists[1] =
338                 thai_make_glyph_tis (font_info, ucs2tis (cluster[1]));
339             glyph_lists[2] =
340                 thai_make_glyph_tis (font_info,
341                         shiftdown_tone_ad (ucs2tis (cluster[2]), shaping_table));
342             return 3;
343           }
344         else if (is_char_type (cluster[0], SpltTailCons) &&
345                  is_char_type (cluster[1], BelowVowel) &&
346                  is_char_type (cluster[2], AboveDiac|Tone))
347           {
348             glyph_lists[0] = thai_make_glyph_tis (font_info,
349                                 tailcutcons (ucs2tis (cluster[0]), shaping_table));
350             glyph_lists[1] =
351                 thai_make_glyph_tis (font_info, ucs2tis (cluster[1]));
352             glyph_lists[2] = thai_make_glyph_tis (font_info,
353                                 shiftdown_tone_ad (ucs2tis (cluster[2]), shaping_table));
354             return 3;
355           }
356         else if (is_char_type (cluster[0], BotTailCons) &&
357                  is_char_type (cluster[1], BelowVowel) &&
358                  is_char_type (cluster[2], AboveDiac|Tone))
359           {
360             glyph_lists[0] =
361                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
362             glyph_lists[1] = thai_make_glyph_tis (font_info,
363                                 shiftdown_bv_bd (ucs2tis (cluster[1]), shaping_table));
364             glyph_lists[2] = thai_make_glyph_tis (font_info,
365                                 shiftdown_tone_ad (ucs2tis (cluster[2]), shaping_table));
366             return 3;
367           }
368         else
369           {
370             glyph_lists[0] =
371                 thai_make_glyph_tis (font_info, ucs2tis (cluster[0]));
372             glyph_lists[1] =
373                 thai_make_glyph_tis (font_info, ucs2tis (cluster[1]));
374             glyph_lists[2] =
375                 thai_make_glyph_tis (font_info, ucs2tis (cluster[2]));
376             return 3;
377           }
378       break;
379
380       default: /* e.g. Lao cluster with below cons + upper/lower vowel + tone */
381         {
382           gint i;
383           for (i = 0; i < num_chrs; i++)
384             glyph_lists[i] = thai_make_glyph_tis (font_info, ucs2tis (cluster[i]));
385           return num_chrs;
386         }
387     }
388
389     return 0;
390 }
391
392 static gint
393 get_glyphs_list (ThaiFontInfo   *font_info,
394                  PangoScript     script,
395                  gunichar       *cluster,
396                  gint           num_chrs,
397                  PangoGlyph     *glyph_lists)
398 {
399   gint i;
400
401   switch ((int) script)
402     {
403       case PANGO_SCRIPT_THAI:
404         switch (font_info->font_set)
405           {
406             default:
407             case THAI_FONT_NONE:
408               for (i=0; i < num_chrs; i++)
409                 glyph_lists[i] = thai_make_unknown_glyph (font_info, cluster[i]);
410               return num_chrs;
411
412             case THAI_FONT_TIS:
413               /* TIS620-0 + Wtt2.0 Extension
414                */
415               return get_adjusted_glyphs_list (font_info, cluster,
416                       num_chrs, glyph_lists, &tis620_0_shape_table);
417
418             case THAI_FONT_TIS_MAC:
419               /* MacIntosh Extension
420                */
421               return get_adjusted_glyphs_list (font_info, cluster,
422                       num_chrs, glyph_lists, &Mac_shape_table);
423
424             case THAI_FONT_TIS_WIN:
425               /* Microsoft Extension
426                */
427               return get_adjusted_glyphs_list (font_info, cluster,
428                       num_chrs, glyph_lists, &Win_shape_table);
429           }
430         break;
431
432       case PANGO_SCRIPT_LAO:
433         return get_adjusted_glyphs_list (font_info, cluster,
434                 num_chrs, glyph_lists, &Lao_shape_table);
435
436       default:
437         for (i=0; i < num_chrs; i++)
438           glyph_lists[i] = thai_make_unknown_glyph (font_info, cluster[i]);
439         return num_chrs;
440     }
441
442   return 0;                     /* Quiet GCC */
443 }
444
445 static void
446 add_cluster (ThaiFontInfo       *font_info,
447              PangoScript         script,
448              PangoGlyphString   *glyphs,
449              gint               cluster_start,
450              gunichar           *cluster,
451              gint               num_chrs)
452 {
453   PangoGlyph glyphs_list[MAX_GLYPHS];
454   gint num_glyphs;
455   gint i;
456
457   if (isthai (cluster[0]))
458     {
459       num_glyphs = get_glyphs_list(font_info, script, cluster, num_chrs, glyphs_list);
460       for (i=0; i<num_glyphs; i++)
461         add_glyph (font_info, glyphs, cluster_start, glyphs_list[i],
462                    i == 0 ? FALSE : TRUE);
463     }
464   else if (islao (cluster[0]))
465     {
466       num_glyphs = get_glyphs_list(font_info, script, cluster, num_chrs, glyphs_list);
467       for (i=0; i<num_glyphs; i++)
468         add_glyph (font_info, glyphs, cluster_start, glyphs_list[i],
469                    i == 0 ? FALSE : TRUE);
470     }
471   else
472     {
473       g_assert (num_chrs == 1);
474       add_glyph (font_info, glyphs, cluster_start,
475                  thai_make_glyph_uni (font_info, cluster[0]),
476                  FALSE);
477     }
478 }
479
480 static gboolean
481 is_wtt_composible (gunichar cur_wc, gunichar nxt_wc)
482 {
483   switch (TAC_compose_input(cur_wc, nxt_wc))
484     {
485       case 'A':
486       case 'S':
487       case 'R':
488       case 'X':
489         return FALSE;
490
491       case 'C':
492         return TRUE;
493     }
494
495   g_assert_not_reached ();
496   return FALSE;
497 }
498
499 static const char *
500 get_next_cluster(const char     *text,
501                  gint           length,
502                  gunichar       *cluster,
503                  gint           *num_chrs)
504 {
505   PangoScript cluster_script = PANGO_SCRIPT_INVALID_CODE, cur_script;
506   const char *p;
507   gint n_chars = 0;
508   gunichar current;
509
510   for (p = text; p < text + length; p = g_utf8_next_char (p))
511     {
512       current = g_utf8_get_char (p);
513       cur_script = pango_script_for_unichar (current);
514       if (cluster_script == PANGO_SCRIPT_INVALID_CODE)
515         cluster_script = cur_script;
516       if (cur_script != cluster_script ||
517           (n_chars > 0 &&
518            !is_wtt_composible (cluster[n_chars - 1], current)))
519         break;
520       cluster[n_chars++] = current;
521     }
522
523   *num_chrs = n_chars;
524   return p;
525 }
526
527 void
528 thai_set_glyphs (ThaiFontInfo     *font_info,
529                  const char       *text,
530                  gint              length,
531                  PangoScript       script,
532                  PangoGlyphString *glyphs)
533 {
534   gint n_chars;
535   const char *p;
536   const char *log_cluster;
537   gunichar cluster[MAX_CLUSTER_CHRS];
538
539   pango_glyph_string_set_size (glyphs, 0);
540
541   p = text;
542   while (p < text + length)
543     {
544         log_cluster = p;
545         p = get_next_cluster (p, text + length - p, cluster, &n_chars);
546         add_cluster (font_info, script, glyphs, log_cluster - text, cluster, n_chars);
547     }
548 }