Towards normalization
[framework/uifw/harfbuzz.git] / src / hb-ot-shape-complex-arabic.cc
1 /*
2  * Copyright © 2010  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Behdad Esfahbod
25  */
26
27 #include "hb-ot-shape-complex-private.hh"
28
29 HB_BEGIN_DECLS
30
31
32 /* buffer var allocations */
33 #define arabic_shaping_action() var2.u32 /* arabic shaping action */
34
35
36 /*
37  * Bits used in the joining tables
38  */
39 enum {
40   JOINING_TYPE_U                = 0,
41   JOINING_TYPE_R                = 1,
42   JOINING_TYPE_D                = 2,
43   JOINING_TYPE_C                = JOINING_TYPE_D,
44   JOINING_GROUP_ALAPH           = 3,
45   JOINING_GROUP_DALATH_RISH     = 4,
46   NUM_STATE_MACHINE_COLS        = 5,
47
48   /* We deliberately don't have a JOINING_TYPE_L since that's unused in Unicode. */
49
50   JOINING_TYPE_T = 6,
51   JOINING_TYPE_X = 7  /* means: use general-category to choose between U or T. */
52 };
53
54 /*
55  * Joining types:
56  */
57
58 #include "hb-ot-shape-complex-arabic-table.hh"
59
60 static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat)
61 {
62   /* TODO Macroize the magic bit operations */
63
64   if (likely (hb_codepoint_in_range (u, JOINING_TABLE_FIRST, JOINING_TABLE_LAST))) {
65     unsigned int j_type = joining_table[u - JOINING_TABLE_FIRST];
66     if (likely (j_type != JOINING_TYPE_X))
67       return j_type;
68   }
69
70   /* Mongolian joining data is not in ArabicJoining.txt yet */
71   if (unlikely (hb_codepoint_in_range (u, 0x1800, 0x18AF)))
72   {
73     /* All letters, SIBE SYLLABLE BOUNDARY MARKER, and NIRUGU are D */
74     if (gen_cat == HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER || u == 0x1807 || u == 0x180A)
75       return JOINING_TYPE_D;
76   }
77
78   if (unlikely (hb_codepoint_in_range (u, 0x200C, 0x200D))) {
79     return u == 0x200C ? JOINING_TYPE_U : JOINING_TYPE_C;
80   }
81
82   return (FLAG(gen_cat) & (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT))) ?
83          JOINING_TYPE_T : JOINING_TYPE_U;
84 }
85
86
87
88 static const hb_tag_t arabic_syriac_features[] =
89 {
90   HB_TAG('i','n','i','t'),
91   HB_TAG('m','e','d','i'),
92   HB_TAG('f','i','n','a'),
93   HB_TAG('i','s','o','l'),
94   /* Syriac */
95   HB_TAG('m','e','d','2'),
96   HB_TAG('f','i','n','2'),
97   HB_TAG('f','i','n','3'),
98   HB_TAG_NONE
99 };
100
101
102 /* Same order as the feature array */
103 enum {
104   INIT,
105   MEDI,
106   FINA,
107   ISOL,
108
109   /* Syriac */
110   MED2,
111   FIN2,
112   FIN3,
113
114   NONE,
115
116   COMMON_NUM_FEATURES = 4,
117   SYRIAC_NUM_FEATURES = 7,
118   TOTAL_NUM_FEATURES = NONE
119 };
120
121 static const struct arabic_state_table_entry {
122         uint8_t prev_action;
123         uint8_t curr_action;
124         uint16_t next_state;
125 } arabic_state_table[][NUM_STATE_MACHINE_COLS] =
126 {
127   /*   jt_U,          jt_R,          jt_D,          jg_ALAPH,      jg_DALATH_RISH */
128
129   /* State 0: prev was U, not willing to join. */
130   { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, },
131
132   /* State 1: prev was R or ISOL/ALAPH, not willing to join. */
133   { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, },
134
135   /* State 2: prev was D/ISOL, willing to join. */
136   { {NONE,NONE,0}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, },
137
138   /* State 3: prev was D/FINA, willing to join. */
139   { {NONE,NONE,0}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,FINA,6}, },
140
141   /* State 4: prev was FINA ALAPH, not willing to join. */
142   { {NONE,NONE,0}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, },
143
144   /* State 5: prev was FIN2/FIN3 ALAPH, not willing to join. */
145   { {NONE,NONE,0}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, },
146
147   /* State 6: prev was DALATH/RISH, not willing to join. */
148   { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, }
149 };
150
151
152
153 void
154 _hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props)
155 {
156   /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together,
157    * then rlig and calt each in their own stage.  This makes IranNastaliq's ALLAH
158    * ligature work correctly. It's unfortunate though...
159    *
160    * This also makes Arial Bold in Windows7 work.  See:
161    * https://bugzilla.mozilla.org/show_bug.cgi?id=644184
162    *
163    * TODO: Add test cases for these two.
164    */
165
166   map->add_bool_feature (HB_TAG('c','c','m','p'));
167
168   map->add_gsub_pause (NULL, NULL);
169
170   unsigned int num_features = props->script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
171   for (unsigned int i = 0; i < num_features; i++)
172     map->add_bool_feature (arabic_syriac_features[i], false);
173
174   map->add_gsub_pause (NULL, NULL);
175
176   map->add_bool_feature (HB_TAG('r','l','i','g'));
177   map->add_gsub_pause (NULL, NULL);
178
179   map->add_bool_feature (HB_TAG('c','a','l','t'));
180   map->add_gsub_pause (NULL, NULL);
181
182   /* ArabicOT spec enables 'cswh' for Arabic where as for basic shaper it's disabled by default. */
183   map->add_bool_feature (HB_TAG('c','s','w','h'));
184 }
185
186 void
187 _hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer)
188 {
189   unsigned int count = buffer->len;
190   unsigned int prev = 0, state = 0;
191
192   for (unsigned int i = 0; i < count; i++)
193   {
194     unsigned int this_type = get_joining_type (buffer->info[i].codepoint, (hb_unicode_general_category_t) buffer->info[i].general_category());
195
196     if (unlikely (this_type == JOINING_TYPE_T)) {
197       buffer->info[i].arabic_shaping_action() = NONE;
198       continue;
199     }
200
201     const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
202
203     if (entry->prev_action != NONE)
204       buffer->info[prev].arabic_shaping_action() = entry->prev_action;
205
206     buffer->info[i].arabic_shaping_action() = entry->curr_action;
207
208     prev = i;
209     state = entry->next_state;
210   }
211
212   hb_mask_t mask_array[TOTAL_NUM_FEATURES + 1] = {0};
213   unsigned int num_masks = buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
214   for (unsigned int i = 0; i < num_masks; i++)
215     mask_array[i] = map->get_1_mask (arabic_syriac_features[i]);
216
217   for (unsigned int i = 0; i < count; i++)
218     buffer->info[i].mask |= mask_array[buffer->info[i].arabic_shaping_action()];
219 }
220
221
222 HB_END_DECLS