hb-ot-shape-normalize.cc revision 64426ec73a987bfe1e71a293ee195f268897e8d6
1655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod/* 211138ccff71f442da1fcf64faa0e1d22e083e775Behdad Esfahbod * Copyright © 2011,2012 Google, Inc. 3655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * 4655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * This is part of HarfBuzz, a text shaping library. 5655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * 6655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * Permission is hereby granted, without written agreement and without 7655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * license or royalty fees, to use, copy, modify, and distribute this 8655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * software and its documentation for any purpose, provided that the 9655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * above copyright notice and the following two paragraphs appear in 10655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * all copies of this software. 11655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * 12655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * DAMAGE. 17655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * 18655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * 24655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod * Google Author(s): Behdad Esfahbod 25655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod */ 26655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod 2711138ccff71f442da1fcf64faa0e1d22e083e775Behdad Esfahbod#include "hb-ot-shape-normalize-private.hh" 280736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod#include "hb-ot-shape-complex-private.hh" 29655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod#include "hb-ot-shape-private.hh" 30655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod 31655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod 325d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod/* 335d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * HIGHLEVEL DESIGN: 345d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * 355d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * This file exports one main function: _hb_ot_shape_normalize(). 365d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * 375d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * This function closely reflects the Unicode Normalization Algorithm, 38c346671b6b9b05fa51b95c16212eb29ac69510faBehdad Esfahbod * yet it's different. 39c346671b6b9b05fa51b95c16212eb29ac69510faBehdad Esfahbod * 40c346671b6b9b05fa51b95c16212eb29ac69510faBehdad Esfahbod * Each shaper specifies whether it prefers decomposed (NFD) or composed (NFC). 41c346671b6b9b05fa51b95c16212eb29ac69510faBehdad Esfahbod * The logic however tries to use whatever the font can support. 425d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * 435d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * In general what happens is that: each grapheme is decomposed in a chain 44947c9a778c0d4b428b58806f98c34ede59b7439cBehdad Esfahbod * of 1:2 decompositions, marks reordered, and then recomposed if desired, 455d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * so far it's like Unicode Normalization. However, the decomposition and 465d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * recomposition only happens if the font supports the resulting characters. 475d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * 485d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * The goals are: 495d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * 505d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * - Try to render all canonically equivalent strings similarly. To really 515d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * achieve this we have to always do the full decomposition and then 525d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * selectively recompose from there. It's kinda too expensive though, so 535d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * we skip some cases. For example, if composed is desired, we simply 545d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * don't touch 1-character clusters that are supported by the font, even 555d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * though their NFC may be different. 565d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * 575d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * - When a font has a precomposed character for a sequence but the 'ccmp' 58947c9a778c0d4b428b58806f98c34ede59b7439cBehdad Esfahbod * feature in the font is not adequate, use the precomposed character 595d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * which typically has better mark positioning. 605d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * 6155deff7595ef357d000fef83559c74c9f8acad00Behdad Esfahbod * - When a font does not support a combining mark, but supports it precomposed 62c346671b6b9b05fa51b95c16212eb29ac69510faBehdad Esfahbod * with previous base, use that. This needs the itemizer to have this 63e3b2e077f549b04779c08a9fedb1f35b9f11075cBehdad Esfahbod * knowledge too. We need to provide assistance to the itemizer. 6455deff7595ef357d000fef83559c74c9f8acad00Behdad Esfahbod * 655d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * - When a font does not support a character but supports its decomposition, 66378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod * well, use the decomposition (preferring the canonical decomposition, but 6784186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * falling back to the compatibility decomposition if necessary). The 6884186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * compatibility decomposition is really nice to have, for characters like 6984186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * ellipsis, or various-sized space characters. 705d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod * 7184186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * - The complex shapers can customize the compose and decompose functions to 7284186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * offload some of their requirements to the normalizer. For example, the 7384186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * Indic shaper may want to disallow recomposing of two matras. 7484186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * 7584186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * - We try compatibility decomposition if decomposing through canonical 7684186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * decomposition alone failed to find a sequence that the font supports. 7784186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * We don't try compatibility decomposition recursively during the canonical 7884186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * decomposition phase. This has minimal impact. There are only a handful 7984186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * of Greek letter that have canonical decompositions that include characters 8084186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * with compatibility decomposition. Those can be found using this command: 8184186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * 8284186a64004e5dcd2ce98b564d0e0a09aa5d68b2Behdad Esfahbod * egrep "`echo -n ';('; grep ';<' UnicodeData.txt | cut -d';' -f1 | tr '\n' '|'; echo ') '`" UnicodeData.txt 835d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod */ 845d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod 85eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbodstatic bool 86eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahboddecompose_unicode (const hb_ot_shape_normalize_context_t *c, 870736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_codepoint_t ab, 880736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_codepoint_t *a, 890736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_codepoint_t *b) 90428dfcab6634ff264570a0a5d715efb8048c3db5Behdad Esfahbod{ 91eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod return c->unicode->decompose (ab, a, b); 92428dfcab6634ff264570a0a5d715efb8048c3db5Behdad Esfahbod} 93428dfcab6634ff264570a0a5d715efb8048c3db5Behdad Esfahbod 94eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbodstatic bool 95eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbodcompose_unicode (const hb_ot_shape_normalize_context_t *c, 960736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_codepoint_t a, 970736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_codepoint_t b, 980736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_codepoint_t *ab) 99428dfcab6634ff264570a0a5d715efb8048c3db5Behdad Esfahbod{ 100eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod return c->unicode->compose (a, b, ab); 101428dfcab6634ff264570a0a5d715efb8048c3db5Behdad Esfahbod} 102428dfcab6634ff264570a0a5d715efb8048c3db5Behdad Esfahbod 103b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbodstatic inline void 104b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbodset_glyph (hb_glyph_info_t &info, hb_font_t *font) 105b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod{ 10607d682806349aee81f53114778ce0beb23909ed7Behdad Esfahbod font->get_glyph (info.codepoint, 0, &info.glyph_index()); 107b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod} 108b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod 1098d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbodstatic inline void 110b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbodoutput_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph) 111c311d852080b50ffc85e80168de62abb05a6be59Behdad Esfahbod{ 112b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod buffer->cur().glyph_index() = glyph; 1138d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod buffer->output_glyph (unichar); 11499c2695759a6af855d565f4994bbdf220570bb48Behdad Esfahbod _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer->unicode); 115c311d852080b50ffc85e80168de62abb05a6be59Behdad Esfahbod} 11645412523dc295cb5ee12e096bfacb282cc925843Behdad Esfahbod 1178d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbodstatic inline void 118b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbodnext_char (hb_buffer_t *buffer, hb_codepoint_t glyph) 1198d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod{ 120b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod buffer->cur().glyph_index() = glyph; 1218d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod buffer->next_glyph (); 1228d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod} 1238d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod 1248d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbodstatic inline void 1258d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbodskip_char (hb_buffer_t *buffer) 1268d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod{ 1278d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod buffer->skip_glyph (); 1288d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod} 1298d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod 130f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod/* Returns 0 if didn't decompose, number of resulting characters otherwise. */ 131f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbodstatic inline unsigned int 132eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahboddecompose (const hb_ot_shape_normalize_context_t *c, bool shortest, hb_codepoint_t ab) 133655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod{ 134b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod hb_codepoint_t a, b, a_glyph, b_glyph; 135ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod hb_buffer_t * const buffer = c->buffer; 136ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod hb_font_t * const font = c->font; 13745412523dc295cb5ee12e096bfacb282cc925843Behdad Esfahbod 138eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod if (!c->decompose (c, ab, &a, &b) || 139ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod (b && !font->get_glyph (b, 0, &b_glyph))) 140f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return 0; 14145412523dc295cb5ee12e096bfacb282cc925843Behdad Esfahbod 142ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod bool has_a = font->get_glyph (a, 0, &a_glyph); 1434ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod if (shortest && has_a) { 1444ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod /* Output a and b */ 145ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod output_char (buffer, a, a_glyph); 146f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod if (likely (b)) { 147ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod output_char (buffer, b, b_glyph); 148f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return 2; 149f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod } 150f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return 1; 1514ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod } 152655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod 153f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod unsigned int ret; 1540736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod if ((ret = decompose (c, shortest, a))) { 155f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod if (b) { 156ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod output_char (buffer, b, b_glyph); 157f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return ret + 1; 158f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod } 159f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return ret; 1604ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod } 16145412523dc295cb5ee12e096bfacb282cc925843Behdad Esfahbod 1624ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod if (has_a) { 163ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod output_char (buffer, a, a_glyph); 164f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod if (likely (b)) { 165ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod output_char (buffer, b, b_glyph); 166f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return 2; 167f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod } 168f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return 1; 16945412523dc295cb5ee12e096bfacb282cc925843Behdad Esfahbod } 17045412523dc295cb5ee12e096bfacb282cc925843Behdad Esfahbod 171f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return 0; 1725c6f5982d78e2d7fadc2fbb8b4f3a4be9420c59aBehdad Esfahbod} 1735c6f5982d78e2d7fadc2fbb8b4f3a4be9420c59aBehdad Esfahbod 174f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod/* Returns 0 if didn't decompose, number of resulting characters otherwise. */ 175a88a62f70f87563725d47b9b6824565e5d6b78abBehdad Esfahbodstatic inline unsigned int 176eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahboddecompose_compatibility (const hb_ot_shape_normalize_context_t *c, hb_codepoint_t u) 177d6b9c6d20041b4f4fa11befc179aee757c41904dBehdad Esfahbod{ 178378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod unsigned int len, i; 179378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod hb_codepoint_t decomposed[HB_UNICODE_MAX_DECOMPOSITION_LEN]; 180b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod hb_codepoint_t glyphs[HB_UNICODE_MAX_DECOMPOSITION_LEN]; 181378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod 1820736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod len = c->buffer->unicode->decompose_compatibility (u, decomposed); 183378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod if (!len) 184f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return 0; 185378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod 186378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod for (i = 0; i < len; i++) 1870736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod if (!c->font->get_glyph (decomposed[i], 0, &glyphs[i])) 188f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return 0; 189378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod 190378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod for (i = 0; i < len; i++) 1910736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod output_char (c->buffer, decomposed[i], glyphs[i]); 192378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod 193f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod return len; 194d6b9c6d20041b4f4fa11befc179aee757c41904dBehdad Esfahbod} 195d6b9c6d20041b4f4fa11befc179aee757c41904dBehdad Esfahbod 1966e74c64211b6aaac48bae8c87f9420d8dc03fd93Behdad Esfahbodstatic inline void 197eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahboddecompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shortest) 1985c6f5982d78e2d7fadc2fbb8b4f3a4be9420c59aBehdad Esfahbod{ 1990736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_buffer_t * const buffer = c->buffer; 2004ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod hb_codepoint_t glyph; 2014ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod 202378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod /* Kind of a cute waterfall here... */ 2030736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod if (shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph)) 204b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod next_char (buffer, glyph); 2056e74c64211b6aaac48bae8c87f9420d8dc03fd93Behdad Esfahbod else if (decompose (c, shortest, buffer->cur().codepoint)) 2068d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod skip_char (buffer); 2070736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod else if (!shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph)) 208b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod next_char (buffer, glyph); 2096e74c64211b6aaac48bae8c87f9420d8dc03fd93Behdad Esfahbod else if (decompose_compatibility (c, buffer->cur().codepoint)) 2108d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod skip_char (buffer); 21107d682806349aee81f53114778ce0beb23909ed7Behdad Esfahbod else 21207d682806349aee81f53114778ce0beb23909ed7Behdad Esfahbod next_char (buffer, glyph); /* glyph is initialized in earlier branches. */ 213b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod} 214b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod 215b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbodstatic inline void 216eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbodhandle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end) 217b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod{ 2180736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_buffer_t * const buffer = c->buffer; 219ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod hb_font_t * const font = c->font; 220b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod for (; buffer->idx < end - 1;) { 221b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) { 222b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod /* The next two lines are some ugly lines... But work. */ 223ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod if (font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index())) 22479d1007a501fd63c0ba4d51038c513e6b8b94740Behdad Esfahbod { 22579d1007a501fd63c0ba4d51038c513e6b8b94740Behdad Esfahbod buffer->replace_glyphs (2, 1, &buffer->cur().codepoint); 22679d1007a501fd63c0ba4d51038c513e6b8b94740Behdad Esfahbod } 22779d1007a501fd63c0ba4d51038c513e6b8b94740Behdad Esfahbod else 22879d1007a501fd63c0ba4d51038c513e6b8b94740Behdad Esfahbod { 22979d1007a501fd63c0ba4d51038c513e6b8b94740Behdad Esfahbod /* Just pass on the two characters separately, let GSUB do its magic. */ 230ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod set_glyph (buffer->cur(), font); 23179d1007a501fd63c0ba4d51038c513e6b8b94740Behdad Esfahbod buffer->next_glyph (); 232ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod set_glyph (buffer->cur(), font); 23379d1007a501fd63c0ba4d51038c513e6b8b94740Behdad Esfahbod buffer->next_glyph (); 23479d1007a501fd63c0ba4d51038c513e6b8b94740Behdad Esfahbod } 235c7a84917208528040aaf9ad0a9a0b26aabeabc9cBehdad Esfahbod /* Skip any further variation selectors. */ 236c7a84917208528040aaf9ad0a9a0b26aabeabc9cBehdad Esfahbod while (buffer->idx < end && unlikely (buffer->unicode->is_variation_selector (buffer->cur().codepoint))) 237c7a84917208528040aaf9ad0a9a0b26aabeabc9cBehdad Esfahbod { 238ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod set_glyph (buffer->cur(), font); 239c7a84917208528040aaf9ad0a9a0b26aabeabc9cBehdad Esfahbod buffer->next_glyph (); 240c7a84917208528040aaf9ad0a9a0b26aabeabc9cBehdad Esfahbod } 241b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod } else { 242ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod set_glyph (buffer->cur(), font); 243b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod buffer->next_glyph (); 244b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod } 245b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod } 246b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod if (likely (buffer->idx < end)) { 247ac8cd511911c7dca6222d14fa758bff75d601567Behdad Esfahbod set_glyph (buffer->cur(), font); 248b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod buffer->next_glyph (); 249b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod } 250655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod} 251655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod 2526e74c64211b6aaac48bae8c87f9420d8dc03fd93Behdad Esfahbodstatic inline void 253eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahboddecompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end) 254655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod{ 2550736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_buffer_t * const buffer = c->buffer; 2565d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */ 25711138ccff71f442da1fcf64faa0e1d22e083e775Behdad Esfahbod for (unsigned int i = buffer->idx; i < end; i++) 258208f70f0553d73d2908b21b9552298029482a8b9Behdad Esfahbod if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) { 2590736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod handle_variation_selector_cluster (c, end); 2606e74c64211b6aaac48bae8c87f9420d8dc03fd93Behdad Esfahbod return; 261af913c5788e600e36d29f44fe4e77db84cf8c442Behdad Esfahbod } 262d6b9c6d20041b4f4fa11befc179aee757c41904dBehdad Esfahbod 26311138ccff71f442da1fcf64faa0e1d22e083e775Behdad Esfahbod while (buffer->idx < end) 2640736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod decompose_current_character (c, false); 265f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod} 266f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod 2676e74c64211b6aaac48bae8c87f9420d8dc03fd93Behdad Esfahbodstatic inline void 268eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahboddecompose_cluster (const hb_ot_shape_normalize_context_t *c, bool short_circuit, unsigned int end) 269f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod{ 2700736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod if (likely (c->buffer->idx + 1 == end)) 2716e74c64211b6aaac48bae8c87f9420d8dc03fd93Behdad Esfahbod decompose_current_character (c, short_circuit); 272f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod else 2736e74c64211b6aaac48bae8c87f9420d8dc03fd93Behdad Esfahbod decompose_multi_char_cluster (c, end); 274655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod} 275655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod 276f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod 27745d6f29f15f1d2323bcaa2498aed23ff0c8a1567Behdad Esfahbodstatic int 27845d6f29f15f1d2323bcaa2498aed23ff0c8a1567Behdad Esfahbodcompare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) 27945d6f29f15f1d2323bcaa2498aed23ff0c8a1567Behdad Esfahbod{ 280d1deaa2f5bd028e8076265cba92cffa4fa2834acBehdad Esfahbod unsigned int a = _hb_glyph_info_get_modified_combining_class (pa); 281d1deaa2f5bd028e8076265cba92cffa4fa2834acBehdad Esfahbod unsigned int b = _hb_glyph_info_get_modified_combining_class (pb); 28245d6f29f15f1d2323bcaa2498aed23ff0c8a1567Behdad Esfahbod 28345d6f29f15f1d2323bcaa2498aed23ff0c8a1567Behdad Esfahbod return a < b ? -1 : a == b ? 0 : +1; 28445d6f29f15f1d2323bcaa2498aed23ff0c8a1567Behdad Esfahbod} 28545d6f29f15f1d2323bcaa2498aed23ff0c8a1567Behdad Esfahbod 286f4cb4762986a28634fa7de9b706f9d37859b881eBehdad Esfahbod 28745412523dc295cb5ee12e096bfacb282cc925843Behdad Esfahbodvoid 288eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod_hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, 2890736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_buffer_t *buffer, 2900736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod hb_font_t *font) 291655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod{ 2923d6ca0d32e5c6597acfcf59301cb1905586ddb52Behdad Esfahbod hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference; 293eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod const hb_ot_shape_normalize_context_t c = { 294eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod plan, 2950736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod buffer, 2960736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod font, 297eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod buffer->unicode, 298eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode, 299eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod plan->shaper->compose ? plan->shaper->compose : compose_unicode 3000736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod }; 3010736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod 302b85800f9de8976a7418ef9df467d3080c6ab0199Behdad Esfahbod bool short_circuit = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED && 303b85800f9de8976a7418ef9df467d3080c6ab0199Behdad Esfahbod mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT; 30434c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod unsigned int count; 3055d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod 306c311d852080b50ffc85e80168de62abb05a6be59Behdad Esfahbod /* We do a fairly straightforward yet custom normalization process in three 3075389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod * separate rounds: decompose, reorder, recompose (if desired). Currently 3085389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod * this makes two buffer swaps. We can make it faster by moving the last 3095389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod * two rounds into the inner loop for the first round, but it's more readable 3105389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod * this way. */ 3115d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod 31234c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod 3134ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod /* First round, decompose */ 3144ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod 3155389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod buffer->clear_output (); 31634c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod count = buffer->len; 317468e9cb25c9bc14781b7013e447d763f93bf76a3Behdad Esfahbod for (buffer->idx = 0; buffer->idx < count;) 3185d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod { 319655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod unsigned int end; 320468e9cb25c9bc14781b7013e447d763f93bf76a3Behdad Esfahbod for (end = buffer->idx + 1; end < count; end++) 32199c2695759a6af855d565f4994bbdf220570bb48Behdad Esfahbod if (buffer->cur().cluster != buffer->info[end].cluster) 322655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod break; 3235d90a342e319068716429bf7af76c3896b61a0e5Behdad Esfahbod 3246e74c64211b6aaac48bae8c87f9420d8dc03fd93Behdad Esfahbod decompose_cluster (&c, short_circuit, end); 325655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod } 326468e9cb25c9bc14781b7013e447d763f93bf76a3Behdad Esfahbod buffer->swap_buffers (); 3274ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod 32834c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod 3294ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod /* Second round, reorder (inplace) */ 3304ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod 33134c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod count = buffer->len; 33234c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod for (unsigned int i = 0; i < count; i++) 33334c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod { 334d1deaa2f5bd028e8076265cba92cffa4fa2834acBehdad Esfahbod if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0) 33534c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod continue; 33634c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod 33734c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod unsigned int end; 33834c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod for (end = i + 1; end < count; end++) 339d1deaa2f5bd028e8076265cba92cffa4fa2834acBehdad Esfahbod if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0) 34034c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod break; 34134c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod 34234c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod /* We are going to do a bubble-sort. Only do this if the 34334c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod * sequence is short. Doing it on long sequences can result 34434c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod * in an O(n^2) DoS. */ 34534c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod if (end - i > 10) { 34634c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod i = end; 34734c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod continue; 34834c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod } 34934c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod 35045d6f29f15f1d2323bcaa2498aed23ff0c8a1567Behdad Esfahbod hb_bubble_sort (buffer->info + i, end - i, compare_combining_class); 35134c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod 35234c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod i = end; 35334c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod } 35434c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod 3554ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod 356b85800f9de8976a7418ef9df467d3080c6ab0199Behdad Esfahbod if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED) 3575389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod return; 3585389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod 3594ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod /* Third round, recompose */ 36034c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod 3615389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod /* As noted in the comment earlier, we don't try to combine 3625389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod * ccc=0 chars with their previous Starter. */ 3634ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod 3645389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod buffer->clear_output (); 3655389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod count = buffer->len; 3665389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod unsigned int starter = 0; 367b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod buffer->next_glyph (); 3685389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod while (buffer->idx < count) 3695389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod { 3705389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod hb_codepoint_t composed, glyph; 371269de14dda7a86a20917fa9ea6a5864929c41364Behdad Esfahbod if (/* We don't try to compose a non-mark character with it's preceding starter. 372269de14dda7a86a20917fa9ea6a5864929c41364Behdad Esfahbod * This is both an optimization to avoid trying to compose every two neighboring 373269de14dda7a86a20917fa9ea6a5864929c41364Behdad Esfahbod * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul 374269de14dda7a86a20917fa9ea6a5864929c41364Behdad Esfahbod * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */ 375269de14dda7a86a20917fa9ea6a5864929c41364Behdad Esfahbod HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur())) && 376968318455304804dc53045e8ba0cd4d76800c02dBehdad Esfahbod /* If there's anything between the starter and this char, they should have CCC 377968318455304804dc53045e8ba0cd4d76800c02dBehdad Esfahbod * smaller than this character's. */ 378968318455304804dc53045e8ba0cd4d76800c02dBehdad Esfahbod (starter == buffer->out_len - 1 || 37999c2695759a6af855d565f4994bbdf220570bb48Behdad Esfahbod _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) && 380968318455304804dc53045e8ba0cd4d76800c02dBehdad Esfahbod /* And compose. */ 381eba312c8d1b2bbe8cb9b6414e843e78d2c521aa4Behdad Esfahbod c.compose (&c, 3820736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod buffer->out_info[starter].codepoint, 3830736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod buffer->cur().codepoint, 3840736915b8ed789a209205fec762997af3a8af89cBehdad Esfahbod &composed) && 385968318455304804dc53045e8ba0cd4d76800c02dBehdad Esfahbod /* And the font has glyph for the composite. */ 3868fbfda920e0b3bb4ab7afb732826026964b79be9Behdad Esfahbod font->get_glyph (composed, 0, &glyph)) 3875389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod { 388bc8357ea7b4c0d7c715aae353176434fb9460205Behdad Esfahbod /* Composes. */ 389b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod buffer->next_glyph (); /* Copy to out-buffer. */ 390bc8357ea7b4c0d7c715aae353176434fb9460205Behdad Esfahbod if (unlikely (buffer->in_error)) 391bc8357ea7b4c0d7c715aae353176434fb9460205Behdad Esfahbod return; 392bc8357ea7b4c0d7c715aae353176434fb9460205Behdad Esfahbod buffer->merge_out_clusters (starter, buffer->out_len); 3938d1eef3f32fb539de2a72804fa3834acc18daab5Behdad Esfahbod buffer->out_len--; /* Remove the second composable. */ 39464426ec73a987bfe1e71a293ee195f268897e8d6Behdad Esfahbod /* Modify starter and carry on. */ 39564426ec73a987bfe1e71a293ee195f268897e8d6Behdad Esfahbod buffer->out_info[starter].codepoint = composed; 39664426ec73a987bfe1e71a293ee195f268897e8d6Behdad Esfahbod buffer->out_info[starter].glyph_index() = glyph; 397d1deaa2f5bd028e8076265cba92cffa4fa2834acBehdad Esfahbod _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode); 398e02d9257863b49e33ab5942971266349d3c548f6Behdad Esfahbod 3995389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod continue; 4005389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod } 4015389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod 402e02d9257863b49e33ab5942971266349d3c548f6Behdad Esfahbod /* Blocked, or doesn't compose. */ 403b00321ea78793d9b3592b5173a9800e6322424feBehdad Esfahbod buffer->next_glyph (); 404968318455304804dc53045e8ba0cd4d76800c02dBehdad Esfahbod 40599c2695759a6af855d565f4994bbdf220570bb48Behdad Esfahbod if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0) 406968318455304804dc53045e8ba0cd4d76800c02dBehdad Esfahbod starter = buffer->out_len - 1; 4074ff0d2d9dfc4f7e4880a4e964ca9872624508ea0Behdad Esfahbod } 4085389ff4dbc46c76c9483e3c95f22524b60e21166Behdad Esfahbod buffer->swap_buffers (); 40934c22f816808d061a980cffca12de03beb437fa0Behdad Esfahbod 410655586fe5e1fadf2a2ef7826e61ee9a445ffa37aBehdad Esfahbod} 411