c5325e42b53f90dc01983eb68b625f253b168bcc
[platform/upstream/harfbuzz.git] / src / hb-ot-shape-normalize.cc
1 /*
2  * Copyright © 2011,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-normalize-private.hh"
28 #include "hb-ot-shape-complex-private.hh"
29 #include "hb-ot-shape-private.hh"
30
31
32 /*
33  * HIGHLEVEL DESIGN:
34  *
35  * This file exports one main function: _hb_ot_shape_normalize().
36  *
37  * This function closely reflects the Unicode Normalization Algorithm,
38  * yet it's different.
39  *
40  * Each shaper specifies whether it prefers decomposed (NFD) or composed (NFC).
41  * The logic however tries to use whatever the font can support.
42  *
43  * In general what happens is that: each grapheme is decomposed in a chain
44  * of 1:2 decompositions, marks reordered, and then recomposed if desired,
45  * so far it's like Unicode Normalization.  However, the decomposition and
46  * recomposition only happens if the font supports the resulting characters.
47  *
48  * The goals are:
49  *
50  *   - Try to render all canonically equivalent strings similarly.  To really
51  *     achieve this we have to always do the full decomposition and then
52  *     selectively recompose from there.  It's kinda too expensive though, so
53  *     we skip some cases.  For example, if composed is desired, we simply
54  *     don't touch 1-character clusters that are supported by the font, even
55  *     though their NFC may be different.
56  *
57  *   - When a font has a precomposed character for a sequence but the 'ccmp'
58  *     feature in the font is not adequate, use the precomposed character
59  *     which typically has better mark positioning.
60  *
61  *   - When a font does not support a combining mark, but supports it precomposed
62  *     with previous base, use that.  This needs the itemizer to have this
63  *     knowledge too.  We need to provide assistance to the itemizer.
64  *
65  *   - When a font does not support a character but supports its decomposition,
66  *     well, use the decomposition (preferring the canonical decomposition, but
67  *     falling back to the compatibility decomposition if necessary).  The
68  *     compatibility decomposition is really nice to have, for characters like
69  *     ellipsis, or various-sized space characters.
70  *
71  *   - The complex shapers can customize the compose and decompose functions to
72  *     offload some of their requirements to the normalizer.  For example, the
73  *     Indic shaper may want to disallow recomposing of two matras.
74  *
75  *   - We try compatibility decomposition if decomposing through canonical
76  *     decomposition alone failed to find a sequence that the font supports.
77  *     We don't try compatibility decomposition recursively during the canonical
78  *     decomposition phase.  This has minimal impact.  There are only a handful
79  *     of Greek letter that have canonical decompositions that include characters
80  *     with compatibility decomposition.  Those can be found using this command:
81  *
82  *     egrep  "`echo -n ';('; grep ';<' UnicodeData.txt | cut -d';' -f1 | tr '\n' '|'; echo ') '`" UnicodeData.txt
83  */
84
85 static bool
86 decompose_unicode (const hb_ot_shape_normalize_context_t *c,
87                    hb_codepoint_t  ab,
88                    hb_codepoint_t *a,
89                    hb_codepoint_t *b)
90 {
91   return c->unicode->decompose (ab, a, b);
92 }
93
94 static bool
95 compose_unicode (const hb_ot_shape_normalize_context_t *c,
96                  hb_codepoint_t  a,
97                  hb_codepoint_t  b,
98                  hb_codepoint_t *ab)
99 {
100   return c->unicode->compose (a, b, ab);
101 }
102
103 static inline void
104 set_glyph (hb_glyph_info_t &info, hb_font_t *font)
105 {
106   font->get_glyph (info.codepoint, 0, &info.glyph_index());
107 }
108
109 static inline void
110 output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph)
111 {
112   buffer->cur().glyph_index() = glyph;
113   buffer->output_glyph (unichar);
114   _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer->unicode);
115 }
116
117 static inline void
118 next_char (hb_buffer_t *buffer, hb_codepoint_t glyph)
119 {
120   buffer->cur().glyph_index() = glyph;
121   buffer->next_glyph ();
122 }
123
124 static inline void
125 skip_char (hb_buffer_t *buffer)
126 {
127   buffer->skip_glyph ();
128 }
129
130 /* Returns 0 if didn't decompose, number of resulting characters otherwise. */
131 static inline unsigned int
132 decompose (const hb_ot_shape_normalize_context_t *c, bool shortest, hb_codepoint_t ab)
133 {
134   hb_codepoint_t a, b, a_glyph, b_glyph;
135
136   if (!c->decompose (c, ab, &a, &b) ||
137       (b && !c->font->get_glyph (b, 0, &b_glyph)))
138     return 0;
139
140   bool has_a = c->font->get_glyph (a, 0, &a_glyph);
141   if (shortest && has_a) {
142     /* Output a and b */
143     output_char (c->buffer, a, a_glyph);
144     if (likely (b)) {
145       output_char (c->buffer, b, b_glyph);
146       return 2;
147     }
148     return 1;
149   }
150
151   unsigned int ret;
152   if ((ret = decompose (c, shortest, a))) {
153     if (b) {
154       output_char (c->buffer, b, b_glyph);
155       return ret + 1;
156     }
157     return ret;
158   }
159
160   if (has_a) {
161     output_char (c->buffer, a, a_glyph);
162     if (likely (b)) {
163       output_char (c->buffer, b, b_glyph);
164       return 2;
165     }
166     return 1;
167   }
168
169   return 0;
170 }
171
172 /* Returns 0 if didn't decompose, number of resulting characters otherwise. */
173 static inline bool
174 decompose_compatibility (const hb_ot_shape_normalize_context_t *c, hb_codepoint_t u)
175 {
176   unsigned int len, i;
177   hb_codepoint_t decomposed[HB_UNICODE_MAX_DECOMPOSITION_LEN];
178   hb_codepoint_t glyphs[HB_UNICODE_MAX_DECOMPOSITION_LEN];
179
180   len = c->buffer->unicode->decompose_compatibility (u, decomposed);
181   if (!len)
182     return 0;
183
184   for (i = 0; i < len; i++)
185     if (!c->font->get_glyph (decomposed[i], 0, &glyphs[i]))
186       return 0;
187
188   for (i = 0; i < len; i++)
189     output_char (c->buffer, decomposed[i], glyphs[i]);
190
191   return len;
192 }
193
194 /* Returns true if recomposition may be benefitial. */
195 static inline bool
196 decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shortest)
197 {
198   hb_buffer_t * const buffer = c->buffer;
199   hb_codepoint_t glyph;
200   unsigned int len = 1;
201
202   /* Kind of a cute waterfall here... */
203   if (shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph))
204     next_char (buffer, glyph);
205   else if ((len = decompose (c, shortest, buffer->cur().codepoint)))
206     skip_char (buffer);
207   else if (!shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph))
208     next_char (buffer, glyph);
209   else if ((len = decompose_compatibility (c, buffer->cur().codepoint)))
210     skip_char (buffer);
211   else
212     next_char (buffer, glyph); /* glyph is initialized in earlier branches. */
213
214   /*
215    * A recomposition would only be useful if we decomposed into at least three
216    * characters...
217    */
218   return len > 2;
219 }
220
221 static inline void
222 handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end)
223 {
224   hb_buffer_t * const buffer = c->buffer;
225   for (; buffer->idx < end - 1;) {
226     if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) {
227       /* The next two lines are some ugly lines... But work. */
228       c->font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index());
229       buffer->replace_glyphs (2, 1, &buffer->cur().codepoint);
230     } else {
231       set_glyph (buffer->cur(), c->font);
232       buffer->next_glyph ();
233     }
234   }
235   if (likely (buffer->idx < end)) {
236     set_glyph (buffer->cur(), c->font);
237     buffer->next_glyph ();
238   }
239 }
240
241 /* Returns true if recomposition may be benefitial. */
242 static inline bool
243 decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end)
244 {
245   hb_buffer_t * const buffer = c->buffer;
246   /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
247   for (unsigned int i = buffer->idx; i < end; i++)
248     if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) {
249       handle_variation_selector_cluster (c, end);
250       return false;
251     }
252
253   while (buffer->idx < end)
254     decompose_current_character (c, false);
255   /* We can be smarter here and only return true if there are at least two ccc!=0 marks.
256    * But does not matter. */
257   return true;
258 }
259
260 static inline bool
261 decompose_cluster (const hb_ot_shape_normalize_context_t *c, bool short_circuit, unsigned int end)
262 {
263   if (likely (c->buffer->idx + 1 == end))
264     return decompose_current_character (c, short_circuit);
265   else
266     return decompose_multi_char_cluster (c, end);
267 }
268
269
270 static int
271 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
272 {
273   unsigned int a = _hb_glyph_info_get_modified_combining_class (pa);
274   unsigned int b = _hb_glyph_info_get_modified_combining_class (pb);
275
276   return a < b ? -1 : a == b ? 0 : +1;
277 }
278
279
280 void
281 _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
282                         hb_buffer_t *buffer,
283                         hb_font_t *font)
284 {
285   hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference ?
286                                           plan->shaper->normalization_preference (&buffer->props) :
287                                           HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT;
288   const hb_ot_shape_normalize_context_t c = {
289     plan,
290     buffer,
291     font,
292     buffer->unicode,
293     plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode,
294     plan->shaper->compose   ? plan->shaper->compose   : compose_unicode
295   };
296
297   bool short_circuit = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED &&
298                        mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
299   bool can_use_recompose = false;
300   unsigned int count;
301
302   /* We do a fairly straightforward yet custom normalization process in three
303    * separate rounds: decompose, reorder, recompose (if desired).  Currently
304    * this makes two buffer swaps.  We can make it faster by moving the last
305    * two rounds into the inner loop for the first round, but it's more readable
306    * this way. */
307
308
309   /* First round, decompose */
310
311   buffer->clear_output ();
312   count = buffer->len;
313   for (buffer->idx = 0; buffer->idx < count;)
314   {
315     unsigned int end;
316     for (end = buffer->idx + 1; end < count; end++)
317       if (buffer->cur().cluster != buffer->info[end].cluster)
318         break;
319
320     can_use_recompose = decompose_cluster (&c, short_circuit, end) || can_use_recompose;
321   }
322   buffer->swap_buffers ();
323
324
325   if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !can_use_recompose)
326     return; /* Done! */
327
328
329   /* Second round, reorder (inplace) */
330
331   count = buffer->len;
332   for (unsigned int i = 0; i < count; i++)
333   {
334     if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0)
335       continue;
336
337     unsigned int end;
338     for (end = i + 1; end < count; end++)
339       if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0)
340         break;
341
342     /* We are going to do a bubble-sort.  Only do this if the
343      * sequence is short.  Doing it on long sequences can result
344      * in an O(n^2) DoS. */
345     if (end - i > 10) {
346       i = end;
347       continue;
348     }
349
350     hb_bubble_sort (buffer->info + i, end - i, compare_combining_class);
351
352     i = end;
353   }
354
355
356   if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED)
357     return;
358
359   /* Third round, recompose */
360
361   /* As noted in the comment earlier, we don't try to combine
362    * ccc=0 chars with their previous Starter. */
363
364   buffer->clear_output ();
365   count = buffer->len;
366   unsigned int starter = 0;
367   buffer->next_glyph ();
368   while (buffer->idx < count)
369   {
370     hb_codepoint_t composed, glyph;
371     if (/* If mode is NOT COMPOSED_FULL (ie. it's COMPOSED_DIACRITICS), we don't try to
372          * compose a CCC=0 character with it's preceding starter. */
373         (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL ||
374          _hb_glyph_info_get_modified_combining_class (&buffer->cur()) != 0) &&
375         /* If there's anything between the starter and this char, they should have CCC
376          * smaller than this character's. */
377         (starter == buffer->out_len - 1 ||
378          _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) &&
379         /* And compose. */
380         c.compose (&c,
381                    buffer->out_info[starter].codepoint,
382                    buffer->cur().codepoint,
383                    &composed) &&
384         /* And the font has glyph for the composite. */
385         font->get_glyph (composed, 0, &glyph))
386     {
387       /* Composes. */
388       buffer->next_glyph (); /* Copy to out-buffer. */
389       if (unlikely (buffer->in_error))
390         return;
391       buffer->merge_out_clusters (starter, buffer->out_len);
392       buffer->out_len--; /* Remove the second composable. */
393       buffer->out_info[starter].codepoint = composed; /* Modify starter and carry on. */
394       set_glyph (buffer->out_info[starter], font);
395       _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode);
396
397       continue;
398     }
399
400     /* Blocked, or doesn't compose. */
401     buffer->next_glyph ();
402
403     if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0)
404       starter = buffer->out_len - 1;
405   }
406   buffer->swap_buffers ();
407
408 }