LCOV - code coverage report
Current view: top level - gfx/harfbuzz/src - hb-ot-shape-complex-indic.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 0 589 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 29 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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-complex-indic-private.hh"
      28             : #include "hb-ot-layout-private.hh"
      29             : 
      30             : /* buffer var allocations */
      31             : #define indic_category() complex_var_u8_0() /* indic_category_t */
      32             : #define indic_position() complex_var_u8_1() /* indic_position_t */
      33             : 
      34             : 
      35             : /*
      36             :  * Indic shaper.
      37             :  */
      38             : 
      39             : 
      40             : #define IN_HALF_BLOCK(u, Base) (((u) & ~0x7Fu) == (Base))
      41             : 
      42             : #define IS_DEVA(u) (IN_HALF_BLOCK (u, 0x0900u))
      43             : #define IS_BENG(u) (IN_HALF_BLOCK (u, 0x0980u))
      44             : #define IS_GURU(u) (IN_HALF_BLOCK (u, 0x0A00u))
      45             : #define IS_GUJR(u) (IN_HALF_BLOCK (u, 0x0A80u))
      46             : #define IS_ORYA(u) (IN_HALF_BLOCK (u, 0x0B00u))
      47             : #define IS_TAML(u) (IN_HALF_BLOCK (u, 0x0B80u))
      48             : #define IS_TELU(u) (IN_HALF_BLOCK (u, 0x0C00u))
      49             : #define IS_KNDA(u) (IN_HALF_BLOCK (u, 0x0C80u))
      50             : #define IS_MLYM(u) (IN_HALF_BLOCK (u, 0x0D00u))
      51             : #define IS_SINH(u) (IN_HALF_BLOCK (u, 0x0D80u))
      52             : #define IS_KHMR(u) (IN_HALF_BLOCK (u, 0x1780u))
      53             : 
      54             : 
      55             : #define MATRA_POS_LEFT(u)       POS_PRE_M
      56             : #define MATRA_POS_RIGHT(u)      ( \
      57             :                                   IS_DEVA(u) ? POS_AFTER_SUB  : \
      58             :                                   IS_BENG(u) ? POS_AFTER_POST : \
      59             :                                   IS_GURU(u) ? POS_AFTER_POST : \
      60             :                                   IS_GUJR(u) ? POS_AFTER_POST : \
      61             :                                   IS_ORYA(u) ? POS_AFTER_POST : \
      62             :                                   IS_TAML(u) ? POS_AFTER_POST : \
      63             :                                   IS_TELU(u) ? (u <= 0x0C42u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
      64             :                                   IS_KNDA(u) ? (u < 0x0CC3u || u > 0xCD6u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
      65             :                                   IS_MLYM(u) ? POS_AFTER_POST : \
      66             :                                   IS_SINH(u) ? POS_AFTER_SUB  : \
      67             :                                   IS_KHMR(u) ? POS_AFTER_POST : \
      68             :                                   /*default*/  POS_AFTER_SUB    \
      69             :                                 )
      70             : #define MATRA_POS_TOP(u)        ( /* BENG and MLYM don't have top matras. */ \
      71             :                                   IS_DEVA(u) ? POS_AFTER_SUB  : \
      72             :                                   IS_GURU(u) ? POS_AFTER_POST : /* Deviate from spec */ \
      73             :                                   IS_GUJR(u) ? POS_AFTER_SUB  : \
      74             :                                   IS_ORYA(u) ? POS_AFTER_MAIN : \
      75             :                                   IS_TAML(u) ? POS_AFTER_SUB  : \
      76             :                                   IS_TELU(u) ? POS_BEFORE_SUB : \
      77             :                                   IS_KNDA(u) ? POS_BEFORE_SUB : \
      78             :                                   IS_SINH(u) ? POS_AFTER_SUB  : \
      79             :                                   IS_KHMR(u) ? POS_AFTER_POST : \
      80             :                                   /*default*/  POS_AFTER_SUB    \
      81             :                                 )
      82             : #define MATRA_POS_BOTTOM(u)     ( \
      83             :                                   IS_DEVA(u) ? POS_AFTER_SUB  : \
      84             :                                   IS_BENG(u) ? POS_AFTER_SUB  : \
      85             :                                   IS_GURU(u) ? POS_AFTER_POST : \
      86             :                                   IS_GUJR(u) ? POS_AFTER_POST : \
      87             :                                   IS_ORYA(u) ? POS_AFTER_SUB  : \
      88             :                                   IS_TAML(u) ? POS_AFTER_POST : \
      89             :                                   IS_TELU(u) ? POS_BEFORE_SUB : \
      90             :                                   IS_KNDA(u) ? POS_BEFORE_SUB : \
      91             :                                   IS_MLYM(u) ? POS_AFTER_POST : \
      92             :                                   IS_SINH(u) ? POS_AFTER_SUB  : \
      93             :                                   IS_KHMR(u) ? POS_AFTER_POST : \
      94             :                                   /*default*/  POS_AFTER_SUB    \
      95             :                                 )
      96             : 
      97             : static inline indic_position_t
      98           0 : matra_position (hb_codepoint_t u, indic_position_t side)
      99             : {
     100           0 :   switch ((int) side)
     101             :   {
     102           0 :     case POS_PRE_C:     return MATRA_POS_LEFT (u);
     103           0 :     case POS_POST_C:    return MATRA_POS_RIGHT (u);
     104           0 :     case POS_ABOVE_C:   return MATRA_POS_TOP (u);
     105           0 :     case POS_BELOW_C:   return MATRA_POS_BOTTOM (u);
     106             :   };
     107           0 :   return side;
     108             : }
     109             : 
     110             : /* XXX
     111             :  * This is a hack for now.  We should move this data into the main Indic table.
     112             :  * Or completely remove it and just check in the tables.
     113             :  */
     114             : static const hb_codepoint_t ra_chars[] = {
     115             :   0x0930u, /* Devanagari */
     116             :   0x09B0u, /* Bengali */
     117             :   0x09F0u, /* Bengali */
     118             :   0x0A30u, /* Gurmukhi */       /* No Reph */
     119             :   0x0AB0u, /* Gujarati */
     120             :   0x0B30u, /* Oriya */
     121             :   0x0BB0u, /* Tamil */          /* No Reph */
     122             :   0x0C30u, /* Telugu */         /* Reph formed only with ZWJ */
     123             :   0x0CB0u, /* Kannada */
     124             :   0x0D30u, /* Malayalam */      /* No Reph, Logical Repha */
     125             : 
     126             :   0x0DBBu, /* Sinhala */                /* Reph formed only with ZWJ */
     127             : 
     128             :   0x179Au, /* Khmer */          /* No Reph, Visual Repha */
     129             : };
     130             : 
     131             : static inline bool
     132           0 : is_ra (hb_codepoint_t u)
     133             : {
     134           0 :   for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++)
     135           0 :     if (u == ra_chars[i])
     136           0 :       return true;
     137           0 :   return false;
     138             : }
     139             : 
     140             : static inline bool
     141           0 : is_one_of (const hb_glyph_info_t &info, unsigned int flags)
     142             : {
     143             :   /* If it ligated, all bets are off. */
     144           0 :   if (_hb_glyph_info_ligated (&info)) return false;
     145           0 :   return !!(FLAG_SAFE (info.indic_category()) & flags);
     146             : }
     147             : 
     148             : static inline bool
     149           0 : is_joiner (const hb_glyph_info_t &info)
     150             : {
     151           0 :   return is_one_of (info, JOINER_FLAGS);
     152             : }
     153             : 
     154             : static inline bool
     155           0 : is_consonant (const hb_glyph_info_t &info)
     156             : {
     157           0 :   return is_one_of (info, CONSONANT_FLAGS);
     158             : }
     159             : 
     160             : static inline bool
     161           0 : is_halant_or_coeng (const hb_glyph_info_t &info)
     162             : {
     163           0 :   return is_one_of (info, HALANT_OR_COENG_FLAGS);
     164             : }
     165             : 
     166             : static inline void
     167           0 : set_indic_properties (hb_glyph_info_t &info)
     168             : {
     169           0 :   hb_codepoint_t u = info.codepoint;
     170           0 :   unsigned int type = hb_indic_get_categories (u);
     171           0 :   indic_category_t cat = (indic_category_t) (type & 0x7Fu);
     172           0 :   indic_position_t pos = (indic_position_t) (type >> 8);
     173             : 
     174             : 
     175             :   /*
     176             :    * Re-assign category
     177             :    */
     178             : 
     179             :   /* The following act more like the Bindus. */
     180           0 :   if (unlikely (hb_in_range (u, 0x0953u, 0x0954u)))
     181           0 :     cat = OT_SM;
     182             :   /* The following act like consonants. */
     183           0 :   else if (unlikely (hb_in_ranges (u, 0x0A72u, 0x0A73u,
     184             :                                       0x1CF5u, 0x1CF6u)))
     185           0 :     cat = OT_C;
     186             :   /* TODO: The following should only be allowed after a Visarga.
     187             :    * For now, just treat them like regular tone marks. */
     188           0 :   else if (unlikely (hb_in_range (u, 0x1CE2u, 0x1CE8u)))
     189           0 :     cat = OT_A;
     190             :   /* TODO: The following should only be allowed after some of
     191             :    * the nasalization marks, maybe only for U+1CE9..U+1CF1.
     192             :    * For now, just treat them like tone marks. */
     193           0 :   else if (unlikely (u == 0x1CEDu))
     194           0 :     cat = OT_A;
     195             :   /* The following take marks in standalone clusters, similar to Avagraha. */
     196           0 :   else if (unlikely (hb_in_ranges (u, 0xA8F2u, 0xA8F7u,
     197             :                                       0x1CE9u, 0x1CECu,
     198             :                                       0x1CEEu, 0x1CF1u)))
     199             :   {
     200           0 :     cat = OT_Symbol;
     201             :     ASSERT_STATIC ((int) INDIC_SYLLABIC_CATEGORY_AVAGRAHA == OT_Symbol);
     202             :   }
     203           0 :   else if (unlikely (u == 0x17C6u)) cat = OT_N; /* Khmer Bindu doesn't like to be repositioned. */
     204           0 :   else if (unlikely (hb_in_range (u, 0x2010u, 0x2011u)))
     205           0 :                                     cat = OT_PLACEHOLDER;
     206           0 :   else if (unlikely (u == 0x25CCu)) cat = OT_DOTTEDCIRCLE;
     207             : 
     208             : 
     209             :   /*
     210             :    * Re-assign position.
     211             :    */
     212             : 
     213           0 :   if ((FLAG_SAFE (cat) & CONSONANT_FLAGS))
     214             :   {
     215           0 :     pos = POS_BASE_C;
     216           0 :     if (is_ra (u))
     217           0 :       cat = OT_Ra;
     218             :   }
     219           0 :   else if (cat == OT_M)
     220             :   {
     221           0 :     pos = matra_position (u, pos);
     222             :   }
     223           0 :   else if ((FLAG_SAFE (cat) & (FLAG (OT_SM) | FLAG (OT_VD) | FLAG (OT_A) | FLAG (OT_Symbol))))
     224             :   {
     225           0 :     pos = POS_SMVD;
     226             :   }
     227             : 
     228           0 :   if (unlikely (u == 0x0B01u)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
     229             : 
     230             : 
     231             : 
     232           0 :   info.indic_category() = cat;
     233           0 :   info.indic_position() = pos;
     234           0 : }
     235             : 
     236             : /*
     237             :  * Things above this line should ideally be moved to the Indic table itself.
     238             :  */
     239             : 
     240             : 
     241             : /*
     242             :  * Indic configurations.  Note that we do not want to keep every single script-specific
     243             :  * behavior in these tables necessarily.  This should mainly be used for per-script
     244             :  * properties that are cheaper keeping here, than in the code.  Ie. if, say, one and
     245             :  * only one script has an exception, that one script can be if'ed directly in the code,
     246             :  * instead of adding a new flag in these structs.
     247             :  */
     248             : 
     249             : enum base_position_t {
     250             :   BASE_POS_FIRST,
     251             :   BASE_POS_LAST_SINHALA,
     252             :   BASE_POS_LAST
     253             : };
     254             : enum reph_position_t {
     255             :   REPH_POS_AFTER_MAIN  = POS_AFTER_MAIN,
     256             :   REPH_POS_BEFORE_SUB  = POS_BEFORE_SUB,
     257             :   REPH_POS_AFTER_SUB   = POS_AFTER_SUB,
     258             :   REPH_POS_BEFORE_POST = POS_BEFORE_POST,
     259             :   REPH_POS_AFTER_POST  = POS_AFTER_POST,
     260             :   REPH_POS_DONT_CARE   = POS_RA_TO_BECOME_REPH
     261             : };
     262             : enum reph_mode_t {
     263             :   REPH_MODE_IMPLICIT,  /* Reph formed out of initial Ra,H sequence. */
     264             :   REPH_MODE_EXPLICIT,  /* Reph formed out of initial Ra,H,ZWJ sequence. */
     265             :   REPH_MODE_VIS_REPHA, /* Encoded Repha character, no reordering needed. */
     266             :   REPH_MODE_LOG_REPHA  /* Encoded Repha character, needs reordering. */
     267             : };
     268             : enum blwf_mode_t {
     269             :   BLWF_MODE_PRE_AND_POST, /* Below-forms feature applied to pre-base and post-base. */
     270             :   BLWF_MODE_POST_ONLY     /* Below-forms feature applied to post-base only. */
     271             : };
     272             : struct indic_config_t
     273             : {
     274             :   hb_script_t     script;
     275             :   bool            has_old_spec;
     276             :   hb_codepoint_t  virama;
     277             :   base_position_t base_pos;
     278             :   reph_position_t reph_pos;
     279             :   reph_mode_t     reph_mode;
     280             :   blwf_mode_t     blwf_mode;
     281             : };
     282             : 
     283             : static const indic_config_t indic_configs[] =
     284             : {
     285             :   /* Default.  Should be first. */
     286             :   {HB_SCRIPT_INVALID,   false,      0,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
     287             :   {HB_SCRIPT_DEVANAGARI,true, 0x094Du,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
     288             :   {HB_SCRIPT_BENGALI,   true, 0x09CDu,BASE_POS_LAST, REPH_POS_AFTER_SUB,  REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
     289             :   {HB_SCRIPT_GURMUKHI,  true, 0x0A4Du,BASE_POS_LAST, REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
     290             :   {HB_SCRIPT_GUJARATI,  true, 0x0ACDu,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
     291             :   {HB_SCRIPT_ORIYA,     true, 0x0B4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
     292             :   {HB_SCRIPT_TAMIL,     true, 0x0BCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
     293             :   {HB_SCRIPT_TELUGU,    true, 0x0C4Du,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY},
     294             :   {HB_SCRIPT_KANNADA,   true, 0x0CCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY},
     295             :   {HB_SCRIPT_MALAYALAM, true, 0x0D4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST},
     296             :   {HB_SCRIPT_SINHALA,   false,0x0DCAu,BASE_POS_LAST_SINHALA,
     297             :                                                      REPH_POS_AFTER_MAIN, REPH_MODE_EXPLICIT, BLWF_MODE_PRE_AND_POST},
     298             :   {HB_SCRIPT_KHMER,     false,0x17D2u,BASE_POS_FIRST,REPH_POS_DONT_CARE,  REPH_MODE_VIS_REPHA,BLWF_MODE_PRE_AND_POST},
     299             : };
     300             : 
     301             : 
     302             : 
     303             : /*
     304             :  * Indic shaper.
     305             :  */
     306             : 
     307             : struct feature_list_t {
     308             :   hb_tag_t tag;
     309             :   hb_ot_map_feature_flags_t flags;
     310             : };
     311             : 
     312             : static const feature_list_t
     313             : indic_features[] =
     314             : {
     315             :   /*
     316             :    * Basic features.
     317             :    * These features are applied in order, one at a time, after initial_reordering.
     318             :    */
     319             :   {HB_TAG('n','u','k','t'), F_GLOBAL},
     320             :   {HB_TAG('a','k','h','n'), F_GLOBAL},
     321             :   {HB_TAG('r','p','h','f'), F_NONE},
     322             :   {HB_TAG('r','k','r','f'), F_GLOBAL},
     323             :   {HB_TAG('p','r','e','f'), F_NONE},
     324             :   {HB_TAG('b','l','w','f'), F_NONE},
     325             :   {HB_TAG('a','b','v','f'), F_NONE},
     326             :   {HB_TAG('h','a','l','f'), F_NONE},
     327             :   {HB_TAG('p','s','t','f'), F_NONE},
     328             :   {HB_TAG('v','a','t','u'), F_GLOBAL},
     329             :   {HB_TAG('c','j','c','t'), F_GLOBAL},
     330             :   {HB_TAG('c','f','a','r'), F_NONE},
     331             :   /*
     332             :    * Other features.
     333             :    * These features are applied all at once, after final_reordering.
     334             :    * Default Bengali font in Windows for example has intermixed
     335             :    * lookups for init,pres,abvs,blws features.
     336             :    */
     337             :   {HB_TAG('i','n','i','t'), F_NONE},
     338             :   {HB_TAG('p','r','e','s'), F_GLOBAL},
     339             :   {HB_TAG('a','b','v','s'), F_GLOBAL},
     340             :   {HB_TAG('b','l','w','s'), F_GLOBAL},
     341             :   {HB_TAG('p','s','t','s'), F_GLOBAL},
     342             :   {HB_TAG('h','a','l','n'), F_GLOBAL},
     343             :   /* Positioning features, though we don't care about the types. */
     344             :   {HB_TAG('d','i','s','t'), F_GLOBAL},
     345             :   {HB_TAG('a','b','v','m'), F_GLOBAL},
     346             :   {HB_TAG('b','l','w','m'), F_GLOBAL},
     347             : };
     348             : 
     349             : /*
     350             :  * Must be in the same order as the indic_features array.
     351             :  */
     352             : enum {
     353             :   _NUKT,
     354             :   _AKHN,
     355             :   RPHF,
     356             :   _RKRF,
     357             :   PREF,
     358             :   BLWF,
     359             :   ABVF,
     360             :   HALF,
     361             :   PSTF,
     362             :   _VATU,
     363             :   _CJCT,
     364             :   CFAR,
     365             : 
     366             :   INIT,
     367             :   _PRES,
     368             :   _ABVS,
     369             :   _BLWS,
     370             :   _PSTS,
     371             :   _HALN,
     372             :   _DIST,
     373             :   _ABVM,
     374             :   _BLWM,
     375             : 
     376             :   INDIC_NUM_FEATURES,
     377             :   INDIC_BASIC_FEATURES = INIT /* Don't forget to update this! */
     378             : };
     379             : 
     380             : static void
     381             : setup_syllables (const hb_ot_shape_plan_t *plan,
     382             :                  hb_font_t *font,
     383             :                  hb_buffer_t *buffer);
     384             : static void
     385             : initial_reordering (const hb_ot_shape_plan_t *plan,
     386             :                     hb_font_t *font,
     387             :                     hb_buffer_t *buffer);
     388             : static void
     389             : final_reordering (const hb_ot_shape_plan_t *plan,
     390             :                   hb_font_t *font,
     391             :                   hb_buffer_t *buffer);
     392             : static void
     393             : clear_syllables (const hb_ot_shape_plan_t *plan,
     394             :                  hb_font_t *font,
     395             :                  hb_buffer_t *buffer);
     396             : 
     397             : static void
     398           0 : collect_features_indic (hb_ot_shape_planner_t *plan)
     399             : {
     400           0 :   hb_ot_map_builder_t *map = &plan->map;
     401             : 
     402             :   /* Do this before any lookups have been applied. */
     403           0 :   map->add_gsub_pause (setup_syllables);
     404             : 
     405           0 :   map->add_global_bool_feature (HB_TAG('l','o','c','l'));
     406             :   /* The Indic specs do not require ccmp, but we apply it here since if
     407             :    * there is a use of it, it's typically at the beginning. */
     408           0 :   map->add_global_bool_feature (HB_TAG('c','c','m','p'));
     409             : 
     410             : 
     411           0 :   unsigned int i = 0;
     412           0 :   map->add_gsub_pause (initial_reordering);
     413           0 :   for (; i < INDIC_BASIC_FEATURES; i++) {
     414           0 :     map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ);
     415           0 :     map->add_gsub_pause (NULL);
     416             :   }
     417           0 :   map->add_gsub_pause (final_reordering);
     418           0 :   for (; i < INDIC_NUM_FEATURES; i++) {
     419           0 :     map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ);
     420             :   }
     421             : 
     422           0 :   map->add_global_bool_feature (HB_TAG('c','a','l','t'));
     423           0 :   map->add_global_bool_feature (HB_TAG('c','l','i','g'));
     424             : 
     425           0 :   map->add_gsub_pause (clear_syllables);
     426           0 : }
     427             : 
     428             : static void
     429           0 : override_features_indic (hb_ot_shape_planner_t *plan)
     430             : {
     431             :   /* Uniscribe does not apply 'kern' in Khmer. */
     432           0 :   if (hb_options ().uniscribe_bug_compatible)
     433             :   {
     434           0 :     switch ((hb_tag_t) plan->props.script)
     435             :     {
     436             :       case HB_SCRIPT_KHMER:
     437           0 :         plan->map.add_feature (HB_TAG('k','e','r','n'), 0, F_GLOBAL);
     438           0 :         break;
     439             :     }
     440             :   }
     441             : 
     442           0 :   plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
     443           0 : }
     444             : 
     445             : 
     446             : struct would_substitute_feature_t
     447             : {
     448           0 :   inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
     449             :   {
     450           0 :     zero_context = zero_context_;
     451           0 :     map->get_stage_lookups (0/*GSUB*/,
     452             :                             map->get_feature_stage (0/*GSUB*/, feature_tag),
     453           0 :                             &lookups, &count);
     454           0 :   }
     455             : 
     456           0 :   inline bool would_substitute (const hb_codepoint_t *glyphs,
     457             :                                 unsigned int          glyphs_count,
     458             :                                 hb_face_t            *face) const
     459             :   {
     460           0 :     for (unsigned int i = 0; i < count; i++)
     461           0 :       if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
     462           0 :         return true;
     463           0 :     return false;
     464             :   }
     465             : 
     466             :   private:
     467             :   const hb_ot_map_t::lookup_map_t *lookups;
     468             :   unsigned int count;
     469             :   bool zero_context;
     470             : };
     471             : 
     472             : struct indic_shape_plan_t
     473             : {
     474             :   ASSERT_POD ();
     475             : 
     476           0 :   inline bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
     477             :   {
     478           0 :     hb_codepoint_t glyph = virama_glyph;
     479           0 :     if (unlikely (virama_glyph == (hb_codepoint_t) -1))
     480             :     {
     481           0 :       if (!config->virama || !font->get_nominal_glyph (config->virama, &glyph))
     482           0 :         glyph = 0;
     483             :       /* Technically speaking, the spec says we should apply 'locl' to virama too.
     484             :        * Maybe one day... */
     485             : 
     486             :       /* Our get_nominal_glyph() function needs a font, so we can't get the virama glyph
     487             :        * during shape planning...  Instead, overwrite it here.  It's safe.  Don't worry! */
     488           0 :       (const_cast<indic_shape_plan_t *> (this))->virama_glyph = glyph;
     489             :     }
     490             : 
     491           0 :     *pglyph = glyph;
     492           0 :     return glyph != 0;
     493             :   }
     494             : 
     495             :   const indic_config_t *config;
     496             : 
     497             :   bool is_old_spec;
     498             :   hb_codepoint_t virama_glyph;
     499             : 
     500             :   would_substitute_feature_t rphf;
     501             :   would_substitute_feature_t pref;
     502             :   would_substitute_feature_t blwf;
     503             :   would_substitute_feature_t pstf;
     504             : 
     505             :   hb_mask_t mask_array[INDIC_NUM_FEATURES];
     506             : };
     507             : 
     508             : static void *
     509           0 : data_create_indic (const hb_ot_shape_plan_t *plan)
     510             : {
     511           0 :   indic_shape_plan_t *indic_plan = (indic_shape_plan_t *) calloc (1, sizeof (indic_shape_plan_t));
     512           0 :   if (unlikely (!indic_plan))
     513           0 :     return NULL;
     514             : 
     515           0 :   indic_plan->config = &indic_configs[0];
     516           0 :   for (unsigned int i = 1; i < ARRAY_LENGTH (indic_configs); i++)
     517           0 :     if (plan->props.script == indic_configs[i].script) {
     518           0 :       indic_plan->config = &indic_configs[i];
     519           0 :       break;
     520             :     }
     521             : 
     522           0 :   indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2');
     523           0 :   indic_plan->virama_glyph = (hb_codepoint_t) -1;
     524             : 
     525             :   /* Use zero-context would_substitute() matching for new-spec of the main
     526             :    * Indic scripts, and scripts with one spec only, but not for old-specs.
     527             :    * The new-spec for all dual-spec scripts says zero-context matching happens.
     528             :    *
     529             :    * However, testing with Malayalam shows that old and new spec both allow
     530             :    * context.  Testing with Bengali new-spec however shows that it doesn't.
     531             :    * So, the heuristic here is the way it is.  It should *only* be changed,
     532             :    * as we discover more cases of what Windows does.  DON'T TOUCH OTHERWISE.
     533             :    */
     534           0 :   bool zero_context = !indic_plan->is_old_spec && plan->props.script != HB_SCRIPT_MALAYALAM;
     535           0 :   indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f'), zero_context);
     536           0 :   indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), zero_context);
     537           0 :   indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'), zero_context);
     538           0 :   indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'), zero_context);
     539             : 
     540           0 :   for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++)
     541           0 :     indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ?
     542           0 :                                  0 : plan->map.get_1_mask (indic_features[i].tag);
     543             : 
     544           0 :   return indic_plan;
     545             : }
     546             : 
     547             : static void
     548           0 : data_destroy_indic (void *data)
     549             : {
     550           0 :   free (data);
     551           0 : }
     552             : 
     553             : static indic_position_t
     554           0 : consonant_position_from_face (const indic_shape_plan_t *indic_plan,
     555             :                               const hb_codepoint_t consonant,
     556             :                               const hb_codepoint_t virama,
     557             :                               hb_face_t *face)
     558             : {
     559             :   /* For old-spec, the order of glyphs is Consonant,Virama,
     560             :    * whereas for new-spec, it's Virama,Consonant.  However,
     561             :    * some broken fonts (like Free Sans) simply copied lookups
     562             :    * from old-spec to new-spec without modification.
     563             :    * And oddly enough, Uniscribe seems to respect those lookups.
     564             :    * Eg. in the sequence U+0924,U+094D,U+0930, Uniscribe finds
     565             :    * base at 0.  The font however, only has lookups matching
     566             :    * 930,94D in 'blwf', not the expected 94D,930 (with new-spec
     567             :    * table).  As such, we simply match both sequences.  Seems
     568             :    * to work. */
     569           0 :   hb_codepoint_t glyphs[3] = {virama, consonant, virama};
     570           0 :   if (indic_plan->blwf.would_substitute (glyphs  , 2, face) ||
     571           0 :       indic_plan->blwf.would_substitute (glyphs+1, 2, face))
     572           0 :     return POS_BELOW_C;
     573           0 :   if (indic_plan->pstf.would_substitute (glyphs  , 2, face) ||
     574           0 :       indic_plan->pstf.would_substitute (glyphs+1, 2, face))
     575           0 :     return POS_POST_C;
     576           0 :   if (indic_plan->pref.would_substitute (glyphs  , 2, face) ||
     577           0 :       indic_plan->pref.would_substitute (glyphs+1, 2, face))
     578           0 :     return POS_POST_C;
     579           0 :   return POS_BASE_C;
     580             : }
     581             : 
     582             : 
     583             : enum syllable_type_t {
     584             :   consonant_syllable,
     585             :   vowel_syllable,
     586             :   standalone_cluster,
     587             :   symbol_cluster,
     588             :   broken_cluster,
     589             :   non_indic_cluster,
     590             : };
     591             : 
     592             : #include "hb-ot-shape-complex-indic-machine.hh"
     593             : 
     594             : 
     595             : static void
     596           0 : setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
     597             :                    hb_buffer_t              *buffer,
     598             :                    hb_font_t                *font HB_UNUSED)
     599             : {
     600           0 :   HB_BUFFER_ALLOCATE_VAR (buffer, indic_category);
     601           0 :   HB_BUFFER_ALLOCATE_VAR (buffer, indic_position);
     602             : 
     603             :   /* We cannot setup masks here.  We save information about characters
     604             :    * and setup masks later on in a pause-callback. */
     605             : 
     606           0 :   unsigned int count = buffer->len;
     607           0 :   hb_glyph_info_t *info = buffer->info;
     608           0 :   for (unsigned int i = 0; i < count; i++)
     609           0 :     set_indic_properties (info[i]);
     610           0 : }
     611             : 
     612             : static void
     613           0 : setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
     614             :                  hb_font_t *font HB_UNUSED,
     615             :                  hb_buffer_t *buffer)
     616             : {
     617           0 :   find_syllables (buffer);
     618           0 : }
     619             : 
     620             : static int
     621           0 : compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
     622             : {
     623           0 :   int a = pa->indic_position();
     624           0 :   int b = pb->indic_position();
     625             : 
     626           0 :   return a < b ? -1 : a == b ? 0 : +1;
     627             : }
     628             : 
     629             : 
     630             : 
     631             : static void
     632           0 : update_consonant_positions (const hb_ot_shape_plan_t *plan,
     633             :                             hb_font_t         *font,
     634             :                             hb_buffer_t       *buffer)
     635             : {
     636           0 :   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
     637             : 
     638           0 :   if (indic_plan->config->base_pos != BASE_POS_LAST)
     639           0 :     return;
     640             : 
     641             :   hb_codepoint_t virama;
     642           0 :   if (indic_plan->get_virama_glyph (font, &virama))
     643             :   {
     644           0 :     hb_face_t *face = font->face;
     645           0 :     unsigned int count = buffer->len;
     646           0 :     hb_glyph_info_t *info = buffer->info;
     647           0 :     for (unsigned int i = 0; i < count; i++)
     648           0 :       if (info[i].indic_position() == POS_BASE_C)
     649             :       {
     650           0 :         hb_codepoint_t consonant = info[i].codepoint;
     651           0 :         info[i].indic_position() = consonant_position_from_face (indic_plan, consonant, virama, face);
     652             :       }
     653             :   }
     654             : }
     655             : 
     656             : 
     657             : /* Rules from:
     658             :  * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
     659             : 
     660             : static void
     661           0 : initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
     662             :                                        hb_face_t *face,
     663             :                                        hb_buffer_t *buffer,
     664             :                                        unsigned int start, unsigned int end)
     665             : {
     666           0 :   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
     667           0 :   hb_glyph_info_t *info = buffer->info;
     668             : 
     669             : 
     670             :   /* 1. Find base consonant:
     671             :    *
     672             :    * The shaping engine finds the base consonant of the syllable, using the
     673             :    * following algorithm: starting from the end of the syllable, move backwards
     674             :    * until a consonant is found that does not have a below-base or post-base
     675             :    * form (post-base forms have to follow below-base forms), or that is not a
     676             :    * pre-base reordering Ra, or arrive at the first consonant. The consonant
     677             :    * stopped at will be the base.
     678             :    *
     679             :    *   o If the syllable starts with Ra + Halant (in a script that has Reph)
     680             :    *     and has more than one consonant, Ra is excluded from candidates for
     681             :    *     base consonants.
     682             :    */
     683             : 
     684           0 :   unsigned int base = end;
     685           0 :   bool has_reph = false;
     686             : 
     687             :   {
     688             :     /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
     689             :      *    and has more than one consonant, Ra is excluded from candidates for
     690             :      *    base consonants. */
     691           0 :     unsigned int limit = start;
     692           0 :     if (indic_plan->config->reph_pos != REPH_POS_DONT_CARE &&
     693           0 :         indic_plan->mask_array[RPHF] &&
     694           0 :         start + 3 <= end &&
     695             :         (
     696           0 :          (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) ||
     697           0 :          (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && info[start + 2].indic_category() == OT_ZWJ)
     698             :         ))
     699             :     {
     700             :       /* See if it matches the 'rphf' feature. */
     701           0 :       hb_codepoint_t glyphs[3] = {info[start].codepoint,
     702           0 :                                   info[start + 1].codepoint,
     703           0 :                                   indic_plan->config->reph_mode == REPH_MODE_EXPLICIT ?
     704           0 :                                     info[start + 2].codepoint : 0};
     705           0 :       if (indic_plan->rphf.would_substitute (glyphs, 2, face) ||
     706           0 :           (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT &&
     707           0 :            indic_plan->rphf.would_substitute (glyphs, 3, face)))
     708             :       {
     709           0 :         limit += 2;
     710           0 :         while (limit < end && is_joiner (info[limit]))
     711           0 :           limit++;
     712           0 :         base = start;
     713           0 :         has_reph = true;
     714             :       }
     715           0 :     } else if (indic_plan->config->reph_mode == REPH_MODE_LOG_REPHA && info[start].indic_category() == OT_Repha)
     716             :     {
     717           0 :         limit += 1;
     718           0 :         while (limit < end && is_joiner (info[limit]))
     719           0 :           limit++;
     720           0 :         base = start;
     721           0 :         has_reph = true;
     722             :     }
     723             : 
     724           0 :     switch (indic_plan->config->base_pos)
     725             :     {
     726             :       case BASE_POS_LAST:
     727             :       {
     728             :         /* -> starting from the end of the syllable, move backwards */
     729           0 :         unsigned int i = end;
     730           0 :         bool seen_below = false;
     731           0 :         do {
     732           0 :           i--;
     733             :           /* -> until a consonant is found */
     734           0 :           if (is_consonant (info[i]))
     735             :           {
     736             :             /* -> that does not have a below-base or post-base form
     737             :              * (post-base forms have to follow below-base forms), */
     738           0 :             if (info[i].indic_position() != POS_BELOW_C &&
     739           0 :                 (info[i].indic_position() != POS_POST_C || seen_below))
     740             :             {
     741           0 :               base = i;
     742           0 :               break;
     743             :             }
     744           0 :             if (info[i].indic_position() == POS_BELOW_C)
     745           0 :               seen_below = true;
     746             : 
     747             :             /* -> or that is not a pre-base reordering Ra,
     748             :              *
     749             :              * IMPLEMENTATION NOTES:
     750             :              *
     751             :              * Our pre-base reordering Ra's are marked POS_POST_C, so will be skipped
     752             :              * by the logic above already.
     753             :              */
     754             : 
     755             :             /* -> or arrive at the first consonant. The consonant stopped at will
     756             :              * be the base. */
     757           0 :             base = i;
     758             :           }
     759             :           else
     760             :           {
     761             :             /* A ZWJ after a Halant stops the base search, and requests an explicit
     762             :              * half form.
     763             :              * A ZWJ before a Halant, requests a subjoined form instead, and hence
     764             :              * search continues.  This is particularly important for Bengali
     765             :              * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya. */
     766           0 :             if (start < i &&
     767           0 :                 info[i].indic_category() == OT_ZWJ &&
     768           0 :                 info[i - 1].indic_category() == OT_H)
     769           0 :               break;
     770             :           }
     771           0 :         } while (i > limit);
     772             :       }
     773           0 :       break;
     774             : 
     775             :       case BASE_POS_LAST_SINHALA:
     776             :       {
     777             :         /* Sinhala base positioning is slightly different from main Indic, in that:
     778             :          * 1. Its ZWJ behavior is different,
     779             :          * 2. We don't need to look into the font for consonant positions.
     780             :          */
     781             : 
     782           0 :         if (!has_reph)
     783           0 :           base = limit;
     784             : 
     785             :         /* Find the last base consonant that is not blocked by ZWJ.  If there is
     786             :          * a ZWJ right before a base consonant, that would request a subjoined form. */
     787           0 :         for (unsigned int i = limit; i < end; i++)
     788           0 :           if (is_consonant (info[i]))
     789             :           {
     790           0 :             if (limit < i && info[i - 1].indic_category() == OT_ZWJ)
     791             :               break;
     792             :             else
     793           0 :               base = i;
     794             :           }
     795             : 
     796             :         /* Mark all subsequent consonants as below. */
     797           0 :         for (unsigned int i = base + 1; i < end; i++)
     798           0 :           if (is_consonant (info[i]))
     799           0 :             info[i].indic_position() = POS_BELOW_C;
     800             :       }
     801           0 :       break;
     802             : 
     803             :       case BASE_POS_FIRST:
     804             :       {
     805             :         /* The first consonant is always the base. */
     806             : 
     807           0 :         assert (indic_plan->config->reph_mode == REPH_MODE_VIS_REPHA);
     808           0 :         assert (!has_reph);
     809             : 
     810           0 :         base = start;
     811             : 
     812             :         /* Mark all subsequent consonants as below. */
     813           0 :         for (unsigned int i = base + 1; i < end; i++)
     814           0 :           if (is_consonant (info[i]))
     815           0 :             info[i].indic_position() = POS_BELOW_C;
     816             :       }
     817           0 :       break;
     818             :     }
     819             : 
     820             :     /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
     821             :      *    and has more than one consonant, Ra is excluded from candidates for
     822             :      *    base consonants.
     823             :      *
     824             :      *  Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */
     825           0 :     if (has_reph && base == start && limit - base <= 2) {
     826             :       /* Have no other consonant, so Reph is not formed and Ra becomes base. */
     827           0 :       has_reph = false;
     828             :     }
     829             :   }
     830             : 
     831             : 
     832             :   /* 2. Decompose and reorder Matras:
     833             :    *
     834             :    * Each matra and any syllable modifier sign in the cluster are moved to the
     835             :    * appropriate position relative to the consonant(s) in the cluster. The
     836             :    * shaping engine decomposes two- or three-part matras into their constituent
     837             :    * parts before any repositioning. Matra characters are classified by which
     838             :    * consonant in a conjunct they have affinity for and are reordered to the
     839             :    * following positions:
     840             :    *
     841             :    *   o Before first half form in the syllable
     842             :    *   o After subjoined consonants
     843             :    *   o After post-form consonant
     844             :    *   o After main consonant (for above marks)
     845             :    *
     846             :    * IMPLEMENTATION NOTES:
     847             :    *
     848             :    * The normalize() routine has already decomposed matras for us, so we don't
     849             :    * need to worry about that.
     850             :    */
     851             : 
     852             : 
     853             :   /* 3.  Reorder marks to canonical order:
     854             :    *
     855             :    * Adjacent nukta and halant or nukta and vedic sign are always repositioned
     856             :    * if necessary, so that the nukta is first.
     857             :    *
     858             :    * IMPLEMENTATION NOTES:
     859             :    *
     860             :    * We don't need to do this: the normalize() routine already did this for us.
     861             :    */
     862             : 
     863             : 
     864             :   /* Reorder characters */
     865             : 
     866           0 :   for (unsigned int i = start; i < base; i++)
     867           0 :     info[i].indic_position() = MIN (POS_PRE_C, (indic_position_t) info[i].indic_position());
     868             : 
     869           0 :   if (base < end)
     870           0 :     info[base].indic_position() = POS_BASE_C;
     871             : 
     872             :   /* Mark final consonants.  A final consonant is one appearing after a matra,
     873             :    * like in Khmer. */
     874           0 :   for (unsigned int i = base + 1; i < end; i++)
     875           0 :     if (info[i].indic_category() == OT_M) {
     876           0 :       for (unsigned int j = i + 1; j < end; j++)
     877           0 :         if (is_consonant (info[j])) {
     878           0 :           info[j].indic_position() = POS_FINAL_C;
     879           0 :           break;
     880             :         }
     881           0 :       break;
     882             :     }
     883             : 
     884             :   /* Handle beginning Ra */
     885           0 :   if (has_reph)
     886           0 :     info[start].indic_position() = POS_RA_TO_BECOME_REPH;
     887             : 
     888             :   /* For old-style Indic script tags, move the first post-base Halant after
     889             :    * last consonant.
     890             :    *
     891             :    * Reports suggest that in some scripts Uniscribe does this only if there
     892             :    * is *not* a Halant after last consonant already (eg. Kannada), while it
     893             :    * does it unconditionally in other scripts (eg. Malayalam).  We don't
     894             :    * currently know about other scripts, so we single out Malayalam for now.
     895             :    *
     896             :    * Kannada test case:
     897             :    * U+0C9A,U+0CCD,U+0C9A,U+0CCD
     898             :    * With some versions of Lohit Kannada.
     899             :    * https://bugs.freedesktop.org/show_bug.cgi?id=59118
     900             :    *
     901             :    * Malayalam test case:
     902             :    * U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D
     903             :    * With lohit-ttf-20121122/Lohit-Malayalam.ttf
     904             :    */
     905           0 :   if (indic_plan->is_old_spec)
     906             :   {
     907           0 :     bool disallow_double_halants = buffer->props.script != HB_SCRIPT_MALAYALAM;
     908           0 :     for (unsigned int i = base + 1; i < end; i++)
     909           0 :       if (info[i].indic_category() == OT_H)
     910             :       {
     911             :         unsigned int j;
     912           0 :         for (j = end - 1; j > i; j--)
     913           0 :           if (is_consonant (info[j]) ||
     914           0 :               (disallow_double_halants && info[j].indic_category() == OT_H))
     915           0 :             break;
     916           0 :         if (info[j].indic_category() != OT_H && j > i) {
     917             :           /* Move Halant to after last consonant. */
     918           0 :           hb_glyph_info_t t = info[i];
     919           0 :           memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0]));
     920           0 :           info[j] = t;
     921             :         }
     922           0 :         break;
     923             :       }
     924             :   }
     925             : 
     926             :   /* Attach misc marks to previous char to move with them. */
     927             :   {
     928           0 :     indic_position_t last_pos = POS_START;
     929           0 :     for (unsigned int i = start; i < end; i++)
     930             :     {
     931           0 :       if ((FLAG_SAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | HALANT_OR_COENG_FLAGS)))
     932             :       {
     933           0 :         info[i].indic_position() = last_pos;
     934           0 :         if (unlikely (info[i].indic_category() == OT_H &&
     935             :                       info[i].indic_position() == POS_PRE_M))
     936             :         {
     937             :           /*
     938             :            * Uniscribe doesn't move the Halant with Left Matra.
     939             :            * TEST: U+092B,U+093F,U+094DE
     940             :            * We follow.  This is important for the Sinhala
     941             :            * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
     942             :            * where U+0DD9 is a left matra and U+0DCA is the virama.
     943             :            * We don't want to move the virama with the left matra.
     944             :            * TEST: U+0D9A,U+0DDA
     945             :            */
     946           0 :           for (unsigned int j = i; j > start; j--)
     947           0 :             if (info[j - 1].indic_position() != POS_PRE_M) {
     948           0 :               info[i].indic_position() = info[j - 1].indic_position();
     949           0 :               break;
     950             :             }
     951             :         }
     952           0 :       } else if (info[i].indic_position() != POS_SMVD) {
     953           0 :         last_pos = (indic_position_t) info[i].indic_position();
     954             :       }
     955             :     }
     956             :   }
     957             :   /* For post-base consonants let them own anything before them
     958             :    * since the last consonant or matra. */
     959             :   {
     960           0 :     unsigned int last = base;
     961           0 :     for (unsigned int i = base + 1; i < end; i++)
     962           0 :       if (is_consonant (info[i]))
     963             :       {
     964           0 :         for (unsigned int j = last + 1; j < i; j++)
     965           0 :           if (info[j].indic_position() < POS_SMVD)
     966           0 :             info[j].indic_position() = info[i].indic_position();
     967           0 :         last = i;
     968           0 :       } else if (info[i].indic_category() == OT_M)
     969           0 :         last = i;
     970             :   }
     971             : 
     972             : 
     973             :   {
     974             :     /* Use syllable() for sort accounting temporarily. */
     975           0 :     unsigned int syllable = info[start].syllable();
     976           0 :     for (unsigned int i = start; i < end; i++)
     977           0 :       info[i].syllable() = i - start;
     978             : 
     979             :     /* Sit tight, rock 'n roll! */
     980           0 :     hb_stable_sort (info + start, end - start, compare_indic_order);
     981             :     /* Find base again */
     982           0 :     base = end;
     983           0 :     for (unsigned int i = start; i < end; i++)
     984           0 :       if (info[i].indic_position() == POS_BASE_C)
     985             :       {
     986           0 :         base = i;
     987           0 :         break;
     988             :       }
     989             :     /* Things are out-of-control for post base positions, they may shuffle
     990             :      * around like crazy.  In old-spec mode, we move halants around, so in
     991             :      * that case merge all clusters after base.  Otherwise, check the sort
     992             :      * order and merge as needed.
     993             :      * For pre-base stuff, we handle cluster issues in final reordering.
     994             :      *
     995             :      * We could use buffer->sort() for this, if there was no special
     996             :      * reordering of pre-base stuff happening later...
     997             :      */
     998           0 :     if (indic_plan->is_old_spec || end - base > 127)
     999           0 :       buffer->merge_clusters (base, end);
    1000             :     else
    1001             :     {
    1002             :       /* Note!  syllable() is a one-byte field. */
    1003           0 :       for (unsigned int i = base; i < end; i++)
    1004           0 :         if (info[i].syllable() != 255)
    1005             :         {
    1006           0 :           unsigned int max = i;
    1007           0 :           unsigned int j = start + info[i].syllable();
    1008           0 :           while (j != i)
    1009             :           {
    1010           0 :             max = MAX (max, j);
    1011           0 :             unsigned int next = start + info[j].syllable();
    1012           0 :             info[j].syllable() = 255; /* So we don't process j later again. */
    1013           0 :             j = next;
    1014             :           }
    1015           0 :           if (i != max)
    1016           0 :             buffer->merge_clusters (i, max + 1);
    1017             :         }
    1018             :     }
    1019             : 
    1020             :     /* Put syllable back in. */
    1021           0 :     for (unsigned int i = start; i < end; i++)
    1022           0 :       info[i].syllable() = syllable;
    1023             :   }
    1024             : 
    1025             :   /* Setup masks now */
    1026             : 
    1027             :   {
    1028             :     hb_mask_t mask;
    1029             : 
    1030             :     /* Reph */
    1031           0 :     for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++)
    1032           0 :       info[i].mask |= indic_plan->mask_array[RPHF];
    1033             : 
    1034             :     /* Pre-base */
    1035           0 :     mask = indic_plan->mask_array[HALF];
    1036           0 :     if (!indic_plan->is_old_spec &&
    1037           0 :         indic_plan->config->blwf_mode == BLWF_MODE_PRE_AND_POST)
    1038           0 :       mask |= indic_plan->mask_array[BLWF];
    1039           0 :     for (unsigned int i = start; i < base; i++)
    1040           0 :       info[i].mask  |= mask;
    1041             :     /* Base */
    1042           0 :     mask = 0;
    1043           0 :     if (base < end)
    1044           0 :       info[base].mask |= mask;
    1045             :     /* Post-base */
    1046           0 :     mask = indic_plan->mask_array[BLWF] | indic_plan->mask_array[ABVF] | indic_plan->mask_array[PSTF];
    1047           0 :     for (unsigned int i = base + 1; i < end; i++)
    1048           0 :       info[i].mask  |= mask;
    1049             :   }
    1050             : 
    1051           0 :   if (indic_plan->is_old_spec &&
    1052           0 :       buffer->props.script == HB_SCRIPT_DEVANAGARI)
    1053             :   {
    1054             :     /* Old-spec eye-lash Ra needs special handling.  From the
    1055             :      * spec:
    1056             :      *
    1057             :      * "The feature 'below-base form' is applied to consonants
    1058             :      * having below-base forms and following the base consonant.
    1059             :      * The exception is vattu, which may appear below half forms
    1060             :      * as well as below the base glyph. The feature 'below-base
    1061             :      * form' will be applied to all such occurrences of Ra as well."
    1062             :      *
    1063             :      * Test case: U+0924,U+094D,U+0930,U+094d,U+0915
    1064             :      * with Sanskrit 2003 font.
    1065             :      *
    1066             :      * However, note that Ra,Halant,ZWJ is the correct way to
    1067             :      * request eyelash form of Ra, so we wouldbn't inhibit it
    1068             :      * in that sequence.
    1069             :      *
    1070             :      * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915
    1071             :      */
    1072           0 :     for (unsigned int i = start; i + 1 < base; i++)
    1073           0 :       if (info[i  ].indic_category() == OT_Ra &&
    1074           0 :           info[i+1].indic_category() == OT_H  &&
    1075           0 :           (i + 2 == base ||
    1076           0 :            info[i+2].indic_category() != OT_ZWJ))
    1077             :       {
    1078           0 :         info[i  ].mask |= indic_plan->mask_array[BLWF];
    1079           0 :         info[i+1].mask |= indic_plan->mask_array[BLWF];
    1080             :       }
    1081             :   }
    1082             : 
    1083           0 :   unsigned int pref_len = 2;
    1084           0 :   if (indic_plan->mask_array[PREF] && base + pref_len < end)
    1085             :   {
    1086             :     /* Find a Halant,Ra sequence and mark it for pre-base reordering processing. */
    1087           0 :     for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) {
    1088             :       hb_codepoint_t glyphs[2];
    1089           0 :       for (unsigned int j = 0; j < pref_len; j++)
    1090           0 :         glyphs[j] = info[i + j].codepoint;
    1091           0 :       if (indic_plan->pref.would_substitute (glyphs, pref_len, face))
    1092             :       {
    1093           0 :         for (unsigned int j = 0; j < pref_len; j++)
    1094           0 :           info[i++].mask |= indic_plan->mask_array[PREF];
    1095             : 
    1096             :         /* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
    1097             :          * Read the feature spec.
    1098             :          * This allows distinguishing the following cases with MS Khmer fonts:
    1099             :          * U+1784,U+17D2,U+179A,U+17D2,U+1782
    1100             :          * U+1784,U+17D2,U+1782,U+17D2,U+179A
    1101             :          */
    1102           0 :         if (indic_plan->mask_array[CFAR])
    1103           0 :           for (; i < end; i++)
    1104           0 :             info[i].mask |= indic_plan->mask_array[CFAR];
    1105             : 
    1106           0 :         break;
    1107             :       }
    1108             :     }
    1109             :   }
    1110             : 
    1111             :   /* Apply ZWJ/ZWNJ effects */
    1112           0 :   for (unsigned int i = start + 1; i < end; i++)
    1113           0 :     if (is_joiner (info[i])) {
    1114           0 :       bool non_joiner = info[i].indic_category() == OT_ZWNJ;
    1115           0 :       unsigned int j = i;
    1116             : 
    1117           0 :       do {
    1118           0 :         j--;
    1119             : 
    1120             :         /* ZWJ/ZWNJ should disable CJCT.  They do that by simply
    1121             :          * being there, since we don't skip them for the CJCT
    1122             :          * feature (ie. F_MANUAL_ZWJ) */
    1123             : 
    1124             :         /* A ZWNJ disables HALF. */
    1125           0 :         if (non_joiner)
    1126           0 :           info[j].mask &= ~indic_plan->mask_array[HALF];
    1127             : 
    1128           0 :       } while (j > start && !is_consonant (info[j]));
    1129             :     }
    1130           0 : }
    1131             : 
    1132             : static void
    1133           0 : initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan,
    1134             :                                        hb_face_t *face,
    1135             :                                        hb_buffer_t *buffer,
    1136             :                                        unsigned int start, unsigned int end)
    1137             : {
    1138             :   /* We treat placeholder/dotted-circle as if they are consonants, so we
    1139             :    * should just chain.  Only if not in compatibility mode that is... */
    1140             : 
    1141           0 :   if (hb_options ().uniscribe_bug_compatible)
    1142             :   {
    1143             :     /* For dotted-circle, this is what Uniscribe does:
    1144             :      * If dotted-circle is the last glyph, it just does nothing.
    1145             :      * Ie. It doesn't form Reph. */
    1146           0 :     if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE)
    1147           0 :       return;
    1148             :   }
    1149             : 
    1150           0 :   initial_reordering_consonant_syllable (plan, face, buffer, start, end);
    1151             : }
    1152             : 
    1153             : static void
    1154           0 : initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
    1155             :                              hb_face_t *face,
    1156             :                              hb_buffer_t *buffer,
    1157             :                              unsigned int start, unsigned int end)
    1158             : {
    1159           0 :   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
    1160           0 :   switch (syllable_type)
    1161             :   {
    1162             :     case vowel_syllable: /* We made the vowels look like consonants.  So let's call the consonant logic! */
    1163             :     case consonant_syllable:
    1164           0 :      initial_reordering_consonant_syllable (plan, face, buffer, start, end);
    1165           0 :      break;
    1166             : 
    1167             :     case broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */
    1168             :     case standalone_cluster:
    1169           0 :      initial_reordering_standalone_cluster (plan, face, buffer, start, end);
    1170           0 :      break;
    1171             : 
    1172             :     case symbol_cluster:
    1173             :     case non_indic_cluster:
    1174           0 :       break;
    1175             :   }
    1176           0 : }
    1177             : 
    1178             : static inline void
    1179           0 : insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
    1180             :                        hb_font_t *font,
    1181             :                        hb_buffer_t *buffer)
    1182             : {
    1183             :   /* Note: This loop is extra overhead, but should not be measurable. */
    1184           0 :   bool has_broken_syllables = false;
    1185           0 :   unsigned int count = buffer->len;
    1186           0 :   hb_glyph_info_t *info = buffer->info;
    1187           0 :   for (unsigned int i = 0; i < count; i++)
    1188           0 :     if ((info[i].syllable() & 0x0F) == broken_cluster)
    1189             :     {
    1190           0 :       has_broken_syllables = true;
    1191           0 :       break;
    1192             :     }
    1193           0 :   if (likely (!has_broken_syllables))
    1194           0 :     return;
    1195             : 
    1196             : 
    1197             :   hb_codepoint_t dottedcircle_glyph;
    1198           0 :   if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
    1199           0 :     return;
    1200             : 
    1201           0 :   hb_glyph_info_t dottedcircle = {0};
    1202           0 :   dottedcircle.codepoint = 0x25CCu;
    1203           0 :   set_indic_properties (dottedcircle);
    1204           0 :   dottedcircle.codepoint = dottedcircle_glyph;
    1205             : 
    1206           0 :   buffer->clear_output ();
    1207             : 
    1208           0 :   buffer->idx = 0;
    1209           0 :   unsigned int last_syllable = 0;
    1210           0 :   while (buffer->idx < buffer->len && !buffer->in_error)
    1211             :   {
    1212           0 :     unsigned int syllable = buffer->cur().syllable();
    1213           0 :     syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
    1214           0 :     if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
    1215             :     {
    1216           0 :       last_syllable = syllable;
    1217             : 
    1218           0 :       hb_glyph_info_t ginfo = dottedcircle;
    1219           0 :       ginfo.cluster = buffer->cur().cluster;
    1220           0 :       ginfo.mask = buffer->cur().mask;
    1221           0 :       ginfo.syllable() = buffer->cur().syllable();
    1222             :       /* TODO Set glyph_props? */
    1223             : 
    1224             :       /* Insert dottedcircle after possible Repha. */
    1225           0 :       while (buffer->idx < buffer->len && !buffer->in_error &&
    1226           0 :              last_syllable == buffer->cur().syllable() &&
    1227           0 :              buffer->cur().indic_category() == OT_Repha)
    1228           0 :         buffer->next_glyph ();
    1229             : 
    1230           0 :       buffer->output_info (ginfo);
    1231             :     }
    1232             :     else
    1233           0 :       buffer->next_glyph ();
    1234             :   }
    1235             : 
    1236           0 :   buffer->swap_buffers ();
    1237             : }
    1238             : 
    1239             : static void
    1240           0 : initial_reordering (const hb_ot_shape_plan_t *plan,
    1241             :                     hb_font_t *font,
    1242             :                     hb_buffer_t *buffer)
    1243             : {
    1244           0 :   update_consonant_positions (plan, font, buffer);
    1245           0 :   insert_dotted_circles (plan, font, buffer);
    1246             : 
    1247           0 :   foreach_syllable (buffer, start, end)
    1248           0 :     initial_reordering_syllable (plan, font->face, buffer, start, end);
    1249           0 : }
    1250             : 
    1251             : static void
    1252           0 : final_reordering_syllable (const hb_ot_shape_plan_t *plan,
    1253             :                            hb_buffer_t *buffer,
    1254             :                            unsigned int start, unsigned int end)
    1255             : {
    1256           0 :   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
    1257           0 :   hb_glyph_info_t *info = buffer->info;
    1258             : 
    1259             : 
    1260             :   /* This function relies heavily on halant glyphs.  Lots of ligation
    1261             :    * and possibly multiplication substitutions happened prior to this
    1262             :    * phase, and that might have messed up our properties.  Recover
    1263             :    * from a particular case of that where we're fairly sure that a
    1264             :    * class of OT_H is desired but has been lost. */
    1265           0 :   if (indic_plan->virama_glyph)
    1266             :   {
    1267           0 :     unsigned int virama_glyph = indic_plan->virama_glyph;
    1268           0 :     for (unsigned int i = start; i < end; i++)
    1269           0 :       if (info[i].codepoint == virama_glyph &&
    1270           0 :           _hb_glyph_info_ligated (&info[i]) &&
    1271           0 :           _hb_glyph_info_multiplied (&info[i]))
    1272             :       {
    1273             :         /* This will make sure that this glyph passes is_halant_or_coeng() test. */
    1274           0 :         info[i].indic_category() = OT_H;
    1275           0 :         _hb_glyph_info_clear_ligated_and_multiplied (&info[i]);
    1276             :       }
    1277             :   }
    1278             : 
    1279             : 
    1280             :   /* 4. Final reordering:
    1281             :    *
    1282             :    * After the localized forms and basic shaping forms GSUB features have been
    1283             :    * applied (see below), the shaping engine performs some final glyph
    1284             :    * reordering before applying all the remaining font features to the entire
    1285             :    * cluster.
    1286             :    */
    1287             : 
    1288           0 :   bool try_pref = !!indic_plan->mask_array[PREF];
    1289             : 
    1290             :   /* Find base again */
    1291             :   unsigned int base;
    1292           0 :   for (base = start; base < end; base++)
    1293           0 :     if (info[base].indic_position() >= POS_BASE_C)
    1294             :     {
    1295           0 :       if (try_pref && base + 1 < end)
    1296             :       {
    1297           0 :         for (unsigned int i = base + 1; i < end; i++)
    1298           0 :           if ((info[i].mask & indic_plan->mask_array[PREF]) != 0)
    1299             :           {
    1300           0 :             if (!(_hb_glyph_info_substituted (&info[i]) &&
    1301           0 :                   _hb_glyph_info_ligated_and_didnt_multiply (&info[i])))
    1302             :             {
    1303             :               /* Ok, this was a 'pref' candidate but didn't form any.
    1304             :                * Base is around here... */
    1305           0 :               base = i;
    1306           0 :               while (base < end && is_halant_or_coeng (info[base]))
    1307           0 :                 base++;
    1308           0 :               info[base].indic_position() = POS_BASE_C;
    1309             : 
    1310           0 :               try_pref = false;
    1311             :             }
    1312           0 :             break;
    1313             :           }
    1314             :       }
    1315             :       /* For Malayalam, skip over unformed below- (but NOT post-) forms. */
    1316           0 :       if (buffer->props.script == HB_SCRIPT_MALAYALAM)
    1317             :       {
    1318           0 :         for (unsigned int i = base + 1; i < end; i++)
    1319             :         {
    1320           0 :           while (i < end && is_joiner (info[i]))
    1321           0 :             i++;
    1322           0 :           if (i == end || !is_halant_or_coeng (info[i]))
    1323           0 :             break;
    1324           0 :           i++; /* Skip halant. */
    1325           0 :           while (i < end && is_joiner (info[i]))
    1326           0 :             i++;
    1327           0 :           if (i < end && is_consonant (info[i]) && info[i].indic_position() == POS_BELOW_C)
    1328             :           {
    1329           0 :             base = i;
    1330           0 :             info[base].indic_position() = POS_BASE_C;
    1331             :           }
    1332             :         }
    1333             :       }
    1334             : 
    1335           0 :       if (start < base && info[base].indic_position() > POS_BASE_C)
    1336           0 :         base--;
    1337           0 :       break;
    1338             :     }
    1339           0 :   if (base == end && start < base &&
    1340           0 :       is_one_of (info[base - 1], FLAG (OT_ZWJ)))
    1341           0 :     base--;
    1342           0 :   if (base < end)
    1343           0 :     while (start < base &&
    1344           0 :            is_one_of (info[base], (FLAG (OT_N) | HALANT_OR_COENG_FLAGS)))
    1345           0 :       base--;
    1346             : 
    1347             : 
    1348             :   /*   o Reorder matras:
    1349             :    *
    1350             :    *     If a pre-base matra character had been reordered before applying basic
    1351             :    *     features, the glyph can be moved closer to the main consonant based on
    1352             :    *     whether half-forms had been formed. Actual position for the matra is
    1353             :    *     defined as “after last standalone halant glyph, after initial matra
    1354             :    *     position and before the main consonant”. If ZWJ or ZWNJ follow this
    1355             :    *     halant, position is moved after it.
    1356             :    */
    1357             : 
    1358           0 :   if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */
    1359             :   {
    1360             :     /* If we lost track of base, alas, position before last thingy. */
    1361           0 :     unsigned int new_pos = base == end ? base - 2 : base - 1;
    1362             : 
    1363             :     /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
    1364             :      * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
    1365             :      * We want to position matra after them.
    1366             :      */
    1367           0 :     if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
    1368             :     {
    1369           0 :       while (new_pos > start &&
    1370           0 :              !(is_one_of (info[new_pos], (FLAG (OT_M) | HALANT_OR_COENG_FLAGS))))
    1371           0 :         new_pos--;
    1372             : 
    1373             :       /* If we found no Halant we are done.
    1374             :        * Otherwise only proceed if the Halant does
    1375             :        * not belong to the Matra itself! */
    1376           0 :       if (is_halant_or_coeng (info[new_pos]) &&
    1377           0 :           info[new_pos].indic_position() != POS_PRE_M)
    1378             :       {
    1379             :         /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
    1380           0 :         if (new_pos + 1 < end && is_joiner (info[new_pos + 1]))
    1381           0 :           new_pos++;
    1382             :       }
    1383             :       else
    1384           0 :         new_pos = start; /* No move. */
    1385             :     }
    1386             : 
    1387           0 :     if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M)
    1388             :     {
    1389             :       /* Now go see if there's actually any matras... */
    1390           0 :       for (unsigned int i = new_pos; i > start; i--)
    1391           0 :         if (info[i - 1].indic_position () == POS_PRE_M)
    1392             :         {
    1393           0 :           unsigned int old_pos = i - 1;
    1394           0 :           if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */
    1395           0 :             base--;
    1396             : 
    1397           0 :           hb_glyph_info_t tmp = info[old_pos];
    1398           0 :           memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0]));
    1399           0 :           info[new_pos] = tmp;
    1400             : 
    1401             :           /* Note: this merge_clusters() is intentionally *after* the reordering.
    1402             :            * Indic matra reordering is special and tricky... */
    1403           0 :           buffer->merge_clusters (new_pos, MIN (end, base + 1));
    1404             : 
    1405           0 :           new_pos--;
    1406           0 :         }
    1407             :     } else {
    1408           0 :       for (unsigned int i = start; i < base; i++)
    1409           0 :         if (info[i].indic_position () == POS_PRE_M) {
    1410           0 :           buffer->merge_clusters (i, MIN (end, base + 1));
    1411           0 :           break;
    1412             :         }
    1413             :     }
    1414             :   }
    1415             : 
    1416             : 
    1417             :   /*   o Reorder reph:
    1418             :    *
    1419             :    *     Reph’s original position is always at the beginning of the syllable,
    1420             :    *     (i.e. it is not reordered at the character reordering stage). However,
    1421             :    *     it will be reordered according to the basic-forms shaping results.
    1422             :    *     Possible positions for reph, depending on the script, are; after main,
    1423             :    *     before post-base consonant forms, and after post-base consonant forms.
    1424             :    */
    1425             : 
    1426             :   /* Two cases:
    1427             :    *
    1428             :    * - If repha is encoded as a sequence of characters (Ra,H or Ra,H,ZWJ), then
    1429             :    *   we should only move it if the sequence ligated to the repha form.
    1430             :    *
    1431             :    * - If repha is encoded separately and in the logical position, we should only
    1432             :    *   move it if it did NOT ligate.  If it ligated, it's probably the font trying
    1433             :    *   to make it work without the reordering.
    1434             :    */
    1435           0 :   if (start + 1 < end &&
    1436           0 :       info[start].indic_position() == POS_RA_TO_BECOME_REPH &&
    1437           0 :       ((info[start].indic_category() == OT_Repha) ^
    1438           0 :        _hb_glyph_info_ligated_and_didnt_multiply (&info[start])))
    1439             :   {
    1440             :     unsigned int new_reph_pos;
    1441           0 :     reph_position_t reph_pos = indic_plan->config->reph_pos;
    1442             : 
    1443           0 :     assert (reph_pos != REPH_POS_DONT_CARE);
    1444             : 
    1445             :     /*       1. If reph should be positioned after post-base consonant forms,
    1446             :      *          proceed to step 5.
    1447             :      */
    1448           0 :     if (reph_pos == REPH_POS_AFTER_POST)
    1449             :     {
    1450           0 :       goto reph_step_5;
    1451             :     }
    1452             : 
    1453             :     /*       2. If the reph repositioning class is not after post-base: target
    1454             :      *          position is after the first explicit halant glyph between the
    1455             :      *          first post-reph consonant and last main consonant. If ZWJ or ZWNJ
    1456             :      *          are following this halant, position is moved after it. If such
    1457             :      *          position is found, this is the target position. Otherwise,
    1458             :      *          proceed to the next step.
    1459             :      *
    1460             :      *          Note: in old-implementation fonts, where classifications were
    1461             :      *          fixed in shaping engine, there was no case where reph position
    1462             :      *          will be found on this step.
    1463             :      */
    1464             :     {
    1465           0 :       new_reph_pos = start + 1;
    1466           0 :       while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos]))
    1467           0 :         new_reph_pos++;
    1468             : 
    1469           0 :       if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos]))
    1470             :       {
    1471             :         /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
    1472           0 :         if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
    1473           0 :           new_reph_pos++;
    1474           0 :         goto reph_move;
    1475             :       }
    1476             :     }
    1477             : 
    1478             :     /*       3. If reph should be repositioned after the main consonant: find the
    1479             :      *          first consonant not ligated with main, or find the first
    1480             :      *          consonant that is not a potential pre-base reordering Ra.
    1481             :      */
    1482           0 :     if (reph_pos == REPH_POS_AFTER_MAIN)
    1483             :     {
    1484           0 :       new_reph_pos = base;
    1485           0 :       while (new_reph_pos + 1 < end && info[new_reph_pos + 1].indic_position() <= POS_AFTER_MAIN)
    1486           0 :         new_reph_pos++;
    1487           0 :       if (new_reph_pos < end)
    1488           0 :         goto reph_move;
    1489             :     }
    1490             : 
    1491             :     /*       4. If reph should be positioned before post-base consonant, find
    1492             :      *          first post-base classified consonant not ligated with main. If no
    1493             :      *          consonant is found, the target position should be before the
    1494             :      *          first matra, syllable modifier sign or vedic sign.
    1495             :      */
    1496             :     /* This is our take on what step 4 is trying to say (and failing, BADLY). */
    1497           0 :     if (reph_pos == REPH_POS_AFTER_SUB)
    1498             :     {
    1499           0 :       new_reph_pos = base;
    1500           0 :       while (new_reph_pos + 1 < end &&
    1501           0 :              !( FLAG_SAFE (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD))))
    1502           0 :         new_reph_pos++;
    1503           0 :       if (new_reph_pos < end)
    1504           0 :         goto reph_move;
    1505             :     }
    1506             : 
    1507             :     /*       5. If no consonant is found in steps 3 or 4, move reph to a position
    1508             :      *          immediately before the first post-base matra, syllable modifier
    1509             :      *          sign or vedic sign that has a reordering class after the intended
    1510             :      *          reph position. For example, if the reordering position for reph
    1511             :      *          is post-main, it will skip above-base matras that also have a
    1512             :      *          post-main position.
    1513             :      */
    1514             :     reph_step_5:
    1515             :     {
    1516             :       /* Copied from step 2. */
    1517           0 :       new_reph_pos = start + 1;
    1518           0 :       while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos]))
    1519           0 :         new_reph_pos++;
    1520             : 
    1521           0 :       if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos]))
    1522             :       {
    1523             :         /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
    1524           0 :         if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
    1525           0 :           new_reph_pos++;
    1526           0 :         goto reph_move;
    1527             :       }
    1528             :     }
    1529             : 
    1530             :     /*       6. Otherwise, reorder reph to the end of the syllable.
    1531             :      */
    1532             :     {
    1533           0 :       new_reph_pos = end - 1;
    1534           0 :       while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD)
    1535           0 :         new_reph_pos--;
    1536             : 
    1537             :       /*
    1538             :        * If the Reph is to be ending up after a Matra,Halant sequence,
    1539             :        * position it before that Halant so it can interact with the Matra.
    1540             :        * However, if it's a plain Consonant,Halant we shouldn't do that.
    1541             :        * Uniscribe doesn't do this.
    1542             :        * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
    1543             :        */
    1544           0 :       if (!hb_options ().uniscribe_bug_compatible &&
    1545           0 :           unlikely (is_halant_or_coeng (info[new_reph_pos]))) {
    1546           0 :         for (unsigned int i = base + 1; i < new_reph_pos; i++)
    1547           0 :           if (info[i].indic_category() == OT_M) {
    1548             :             /* Ok, got it. */
    1549           0 :             new_reph_pos--;
    1550             :           }
    1551             :       }
    1552           0 :       goto reph_move;
    1553             :     }
    1554             : 
    1555             :     reph_move:
    1556             :     {
    1557             :       /* Move */
    1558           0 :       buffer->merge_clusters (start, new_reph_pos + 1);
    1559           0 :       hb_glyph_info_t reph = info[start];
    1560           0 :       memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
    1561           0 :       info[new_reph_pos] = reph;
    1562             : 
    1563           0 :       if (start < base && base <= new_reph_pos)
    1564           0 :         base--;
    1565             :     }
    1566             :   }
    1567             : 
    1568             : 
    1569             :   /*   o Reorder pre-base reordering consonants:
    1570             :    *
    1571             :    *     If a pre-base reordering consonant is found, reorder it according to
    1572             :    *     the following rules:
    1573             :    */
    1574             : 
    1575           0 :   if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base reordering Ra. */
    1576             :   {
    1577           0 :     for (unsigned int i = base + 1; i < end; i++)
    1578           0 :       if ((info[i].mask & indic_plan->mask_array[PREF]) != 0)
    1579             :       {
    1580             :         /*       1. Only reorder a glyph produced by substitution during application
    1581             :          *          of the <pref> feature. (Note that a font may shape a Ra consonant with
    1582             :          *          the feature generally but block it in certain contexts.)
    1583             :          */
    1584             :         /* Note: We just check that something got substituted.  We don't check that
    1585             :          * the <pref> feature actually did it...
    1586             :          *
    1587             :          * Reorder pref only if it ligated. */
    1588           0 :         if (_hb_glyph_info_ligated_and_didnt_multiply (&info[i]))
    1589             :         {
    1590             :           /*
    1591             :            *       2. Try to find a target position the same way as for pre-base matra.
    1592             :            *          If it is found, reorder pre-base consonant glyph.
    1593             :            *
    1594             :            *       3. If position is not found, reorder immediately before main
    1595             :            *          consonant.
    1596             :            */
    1597             : 
    1598           0 :           unsigned int new_pos = base;
    1599             :           /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
    1600             :            * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
    1601             :            * We want to position matra after them.
    1602             :            */
    1603           0 :           if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
    1604             :           {
    1605           0 :             while (new_pos > start &&
    1606           0 :                    !(is_one_of (info[new_pos - 1], FLAG(OT_M) | HALANT_OR_COENG_FLAGS)))
    1607           0 :               new_pos--;
    1608             : 
    1609             :             /* In Khmer coeng model, a H,Ra can go *after* matras.  If it goes after a
    1610             :              * split matra, it should be reordered to *before* the left part of such matra. */
    1611           0 :             if (new_pos > start && info[new_pos - 1].indic_category() == OT_M)
    1612             :             {
    1613           0 :               unsigned int old_pos = i;
    1614           0 :               for (unsigned int j = base + 1; j < old_pos; j++)
    1615           0 :                 if (info[j].indic_category() == OT_M)
    1616             :                 {
    1617           0 :                   new_pos--;
    1618           0 :                   break;
    1619             :                 }
    1620             :             }
    1621             :           }
    1622             : 
    1623           0 :           if (new_pos > start && is_halant_or_coeng (info[new_pos - 1]))
    1624             :           {
    1625             :             /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
    1626           0 :             if (new_pos < end && is_joiner (info[new_pos]))
    1627           0 :               new_pos++;
    1628             :           }
    1629             : 
    1630             :           {
    1631           0 :             unsigned int old_pos = i;
    1632             : 
    1633           0 :             buffer->merge_clusters (new_pos, old_pos + 1);
    1634           0 :             hb_glyph_info_t tmp = info[old_pos];
    1635           0 :             memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0]));
    1636           0 :             info[new_pos] = tmp;
    1637             : 
    1638           0 :             if (new_pos <= base && base < old_pos)
    1639           0 :               base++;
    1640             :           }
    1641             :         }
    1642             : 
    1643           0 :         break;
    1644             :       }
    1645             :   }
    1646             : 
    1647             : 
    1648             :   /* Apply 'init' to the Left Matra if it's a word start. */
    1649           0 :   if (info[start].indic_position () == POS_PRE_M &&
    1650           0 :       (!start ||
    1651           0 :        !(FLAG_SAFE (_hb_glyph_info_get_general_category (&info[start - 1])) &
    1652             :          FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
    1653           0 :     info[start].mask |= indic_plan->mask_array[INIT];
    1654             : 
    1655             : 
    1656             :   /*
    1657             :    * Finish off the clusters and go home!
    1658             :    */
    1659           0 :   if (hb_options ().uniscribe_bug_compatible)
    1660             :   {
    1661           0 :     switch ((hb_tag_t) plan->props.script)
    1662             :     {
    1663             :       case HB_SCRIPT_TAMIL:
    1664             :       case HB_SCRIPT_SINHALA:
    1665           0 :         break;
    1666             : 
    1667             :       default:
    1668             :         /* Uniscribe merges the entire cluster... Except for Tamil & Sinhala.
    1669             :          * This means, half forms are submerged into the main consonants cluster.
    1670             :          * This is unnecessary, and makes cursor positioning harder, but that's what
    1671             :          * Uniscribe does. */
    1672           0 :         buffer->merge_clusters (start, end);
    1673           0 :         break;
    1674             :     }
    1675             :   }
    1676           0 : }
    1677             : 
    1678             : 
    1679             : static void
    1680           0 : final_reordering (const hb_ot_shape_plan_t *plan,
    1681             :                   hb_font_t *font HB_UNUSED,
    1682             :                   hb_buffer_t *buffer)
    1683             : {
    1684           0 :   unsigned int count = buffer->len;
    1685           0 :   if (unlikely (!count)) return;
    1686             : 
    1687           0 :   foreach_syllable (buffer, start, end)
    1688           0 :     final_reordering_syllable (plan, buffer, start, end);
    1689             : 
    1690           0 :   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
    1691           0 :   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
    1692             : }
    1693             : 
    1694             : 
    1695             : static void
    1696           0 : clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
    1697             :                  hb_font_t *font HB_UNUSED,
    1698             :                  hb_buffer_t *buffer)
    1699             : {
    1700           0 :   hb_glyph_info_t *info = buffer->info;
    1701           0 :   unsigned int count = buffer->len;
    1702           0 :   for (unsigned int i = 0; i < count; i++)
    1703           0 :     info[i].syllable() = 0;
    1704           0 : }
    1705             : 
    1706             : 
    1707             : static bool
    1708           0 : decompose_indic (const hb_ot_shape_normalize_context_t *c,
    1709             :                  hb_codepoint_t  ab,
    1710             :                  hb_codepoint_t *a,
    1711             :                  hb_codepoint_t *b)
    1712             : {
    1713           0 :   switch (ab)
    1714             :   {
    1715             :     /* Don't decompose these. */
    1716           0 :     case 0x0931u  : return false; /* DEVANAGARI LETTER RRA */
    1717           0 :     case 0x0B94u  : return false; /* TAMIL LETTER AU */
    1718             : 
    1719             : 
    1720             :     /*
    1721             :      * Decompose split matras that don't have Unicode decompositions.
    1722             :      */
    1723             : 
    1724             :     /* Khmer */
    1725           0 :     case 0x17BEu  : *a = 0x17C1u; *b= 0x17BEu; return true;
    1726           0 :     case 0x17BFu  : *a = 0x17C1u; *b= 0x17BFu; return true;
    1727           0 :     case 0x17C0u  : *a = 0x17C1u; *b= 0x17C0u; return true;
    1728           0 :     case 0x17C4u  : *a = 0x17C1u; *b= 0x17C4u; return true;
    1729           0 :     case 0x17C5u  : *a = 0x17C1u; *b= 0x17C5u; return true;
    1730             : 
    1731             : #if 0
    1732             :     /* Gujarati */
    1733             :     /* This one has no decomposition in Unicode, but needs no decomposition either. */
    1734             :     /* case 0x0AC9u  : return false; */
    1735             : 
    1736             :     /* Oriya */
    1737             :     case 0x0B57u  : *a = no decomp, -> RIGHT; return true;
    1738             : #endif
    1739             :   }
    1740             : 
    1741           0 :   if ((ab == 0x0DDAu || hb_in_range (ab, 0x0DDCu, 0x0DDEu)))
    1742             :   {
    1743             :     /*
    1744             :      * Sinhala split matras...  Let the fun begin.
    1745             :      *
    1746             :      * These four characters have Unicode decompositions.  However, Uniscribe
    1747             :      * decomposes them "Khmer-style", that is, it uses the character itself to
    1748             :      * get the second half.  The first half of all four decompositions is always
    1749             :      * U+0DD9.
    1750             :      *
    1751             :      * Now, there are buggy fonts, namely, the widely used lklug.ttf, that are
    1752             :      * broken with Uniscribe.  But we need to support them.  As such, we only
    1753             :      * do the Uniscribe-style decomposition if the character is transformed into
    1754             :      * its "sec.half" form by the 'pstf' feature.  Otherwise, we fall back to
    1755             :      * Unicode decomposition.
    1756             :      *
    1757             :      * Note that we can't unconditionally use Unicode decomposition.  That would
    1758             :      * break some other fonts, that are designed to work with Uniscribe, and
    1759             :      * don't have positioning features for the Unicode-style decomposition.
    1760             :      *
    1761             :      * Argh...
    1762             :      *
    1763             :      * The Uniscribe behavior is now documented in the newly published Sinhala
    1764             :      * spec in 2012:
    1765             :      *
    1766             :      *   http://www.microsoft.com/typography/OpenTypeDev/sinhala/intro.htm#shaping
    1767             :      */
    1768             : 
    1769           0 :     const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data;
    1770             : 
    1771             :     hb_codepoint_t glyph;
    1772             : 
    1773           0 :     if (hb_options ().uniscribe_bug_compatible ||
    1774           0 :         (c->font->get_nominal_glyph (ab, &glyph) &&
    1775           0 :          indic_plan->pstf.would_substitute (&glyph, 1, c->font->face)))
    1776             :     {
    1777             :       /* Ok, safe to use Uniscribe-style decomposition. */
    1778           0 :       *a = 0x0DD9u;
    1779           0 :       *b = ab;
    1780           0 :       return true;
    1781             :     }
    1782             :   }
    1783             : 
    1784           0 :   return (bool) c->unicode->decompose (ab, a, b);
    1785             : }
    1786             : 
    1787             : static bool
    1788           0 : compose_indic (const hb_ot_shape_normalize_context_t *c,
    1789             :                hb_codepoint_t  a,
    1790             :                hb_codepoint_t  b,
    1791             :                hb_codepoint_t *ab)
    1792             : {
    1793             :   /* Avoid recomposing split matras. */
    1794           0 :   if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a)))
    1795           0 :     return false;
    1796             : 
    1797             :   /* Composition-exclusion exceptions that we want to recompose. */
    1798           0 :   if (a == 0x09AFu && b == 0x09BCu) { *ab = 0x09DFu; return true; }
    1799             : 
    1800           0 :   return (bool) c->unicode->compose (a, b, ab);
    1801             : }
    1802             : 
    1803             : 
    1804             : const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
    1805             : {
    1806             :   "indic",
    1807             :   collect_features_indic,
    1808             :   override_features_indic,
    1809             :   data_create_indic,
    1810             :   data_destroy_indic,
    1811             :   NULL, /* preprocess_text */
    1812             :   NULL, /* postprocess_glyphs */
    1813             :   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
    1814             :   decompose_indic,
    1815             :   compose_indic,
    1816             :   setup_masks_indic,
    1817             :   NULL, /* disable_otl */
    1818             :   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
    1819             :   false, /* fallback_position */
    1820             : };

Generated by: LCOV version 1.13