Imported Upstream version 0.9.3
[platform/upstream/harfbuzz.git] / src / hb-ot-shape-complex-misc.cc
1 /*
2  * Copyright © 2010,2012  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
30 /* TODO Add kana, and other small shapers here */
31
32
33 /* The default shaper *only* adds additional per-script features.*/
34
35 static const hb_tag_t hangul_features[] =
36 {
37   HB_TAG('l','j','m','o'),
38   HB_TAG('v','j','m','o'),
39   HB_TAG('t','j','m','o'),
40   HB_TAG_NONE
41 };
42
43 static const hb_tag_t tibetan_features[] =
44 {
45   HB_TAG('a','b','v','s'),
46   HB_TAG('b','l','w','s'),
47   HB_TAG('a','b','v','m'),
48   HB_TAG('b','l','w','m'),
49   HB_TAG_NONE
50 };
51
52 static void
53 collect_features_default (hb_ot_shape_planner_t *plan)
54 {
55   const hb_tag_t *script_features = NULL;
56
57   switch ((hb_tag_t) plan->props.script)
58   {
59     /* Unicode-1.1 additions */
60     case HB_SCRIPT_HANGUL:
61       script_features = hangul_features;
62       break;
63
64     /* Unicode-2.0 additions */
65     case HB_SCRIPT_TIBETAN:
66       script_features = tibetan_features;
67       break;
68   }
69
70   for (; script_features && *script_features; script_features++)
71     plan->map.add_bool_feature (*script_features);
72 }
73
74 static hb_ot_shape_normalization_mode_t
75 normalization_preference_default (const hb_ot_shape_plan_t *plan)
76 {
77   switch ((hb_tag_t) plan->props.script)
78   {
79     /* Unicode-1.1 additions */
80     case HB_SCRIPT_HANGUL:
81       return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL;
82   }
83   return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
84 }
85
86 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
87 {
88   "default",
89   collect_features_default,
90   NULL, /* override_features */
91   NULL, /* data_create */
92   NULL, /* data_destroy */
93   NULL, /* preprocess_text */
94   normalization_preference_default,
95   NULL, /* setup_masks */
96   true, /* zero_width_attached_marks */
97 };
98
99
100 /* Thai / Lao shaper */
101
102 static void
103 preprocess_text_thai (const hb_ot_shape_plan_t *plan HB_UNUSED,
104                       hb_buffer_t              *buffer,
105                       hb_font_t                *font HB_UNUSED)
106 {
107   /* The following is NOT specified in the MS OT Thai spec, however, it seems
108    * to be what Uniscribe and other engines implement.  According to Eric Muller:
109    *
110    * When you have a SARA AM, decompose it in NIKHAHIT + SARA AA, *and* move the
111    * NIKHAHIT backwards over any tone mark (0E48-0E4B).
112    *
113    * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32>
114    *
115    * This reordering is legit only when the NIKHAHIT comes from a SARA AM, not
116    * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably
117    * not what a user wanted, but the rendering is nevertheless nikhahit above
118    * chattawa.
119    *
120    * Same for Lao.
121    *
122    * Note:
123    *
124    * Uniscribe also does so below-marks reordering.  Namely, it positions U+0E3A
125    * after U+0E38 and U+0E39.  We do that by modifying the ccc for U+0E3A.
126    * See unicode->modified_combining_class ().  Lao does NOT have a U+0E3A
127    * equivalent.
128    */
129
130
131   /*
132    * Here are the characters of significance:
133    *
134    *                    Thai    Lao
135    * SARA AM:           U+0E33  U+0EB3
136    * SARA AA:           U+0E32  U+0EB2
137    * Nikhahit:          U+0E4D  U+0ECD
138    *
139    * Testing shows that Uniscribe reorder the following marks:
140    * Thai:      <0E31,0E34..0E37,0E47..0E4E>
141    * Lao:       <0EB1,0EB4..0EB7,0EC7..0ECE>
142    *
143    * Note how the Lao versions are the same as Thai + 0x80.
144    */
145
146   /* We only get one script at a time, so a script-agnostic implementation
147    * is adequate here. */
148 #define IS_SARA_AM(x) (((x) & ~0x0080) == 0x0E33)
149 #define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0xE33 + 0xE4D)
150 #define SARA_AA_FROM_SARA_AM(x) ((x) - 1)
151 #define IS_TONE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080, 0x0E34, 0x0E37, 0x0E47, 0x0E4E, 0x0E31, 0x0E31))
152
153   buffer->clear_output ();
154   unsigned int count = buffer->len;
155   for (buffer->idx = 0; buffer->idx < count;)
156   {
157     hb_codepoint_t u = buffer->cur().codepoint;
158     if (likely (!IS_SARA_AM (u))) {
159       buffer->next_glyph ();
160       continue;
161     }
162
163     /* Is SARA AM. Decompose and reorder. */
164     hb_codepoint_t decomposed[2] = {hb_codepoint_t (NIKHAHIT_FROM_SARA_AM (u)),
165                                     hb_codepoint_t (SARA_AA_FROM_SARA_AM (u))};
166     buffer->replace_glyphs (1, 2, decomposed);
167     if (unlikely (buffer->in_error))
168       return;
169
170     /* Ok, let's see... */
171     unsigned int end = buffer->out_len;
172     unsigned int start = end - 2;
173     while (start > 0 && IS_TONE_MARK (buffer->out_info[start - 1].codepoint))
174       start--;
175
176     if (start + 2 < end)
177     {
178       /* Move Nikhahit (end-2) to the beginning */
179       buffer->merge_out_clusters (start, end);
180       hb_glyph_info_t t = buffer->out_info[end - 2];
181       memmove (buffer->out_info + start + 1,
182                buffer->out_info + start,
183                sizeof (buffer->out_info[0]) * (end - start - 2));
184       buffer->out_info[start] = t;
185     }
186     else
187     {
188       /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the
189        * previous cluster. */
190       if (start)
191         buffer->merge_out_clusters (start - 1, end);
192     }
193   }
194   buffer->swap_buffers ();
195 }
196
197 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
198 {
199   "thai",
200   NULL, /* collect_features */
201   NULL, /* override_features */
202   NULL, /* data_create */
203   NULL, /* data_destroy */
204   preprocess_text_thai,
205   NULL, /* normalization_preference */
206   NULL, /* setup_masks */
207   true, /* zero_width_attached_marks */
208 };