| OLD | NEW | 
|---|
| 1 /* | 1 /* | 
| 2  * Copyright © 2010,2012  Google, Inc. | 2  * Copyright © 2010,2012  Google, Inc. | 
| 3  * | 3  * | 
| 4  *  This is part of HarfBuzz, a text shaping library. | 4  *  This is part of HarfBuzz, a text shaping library. | 
| 5  * | 5  * | 
| 6  * Permission is hereby granted, without written agreement and without | 6  * Permission is hereby granted, without written agreement and without | 
| 7  * license or royalty fees, to use, copy, modify, and distribute this | 7  * license or royalty fees, to use, copy, modify, and distribute this | 
| 8  * software and its documentation for any purpose, provided that the | 8  * software and its documentation for any purpose, provided that the | 
| 9  * above copyright notice and the following two paragraphs appear in | 9  * above copyright notice and the following two paragraphs appear in | 
| 10  * all copies of this software. | 10  * all copies of this software. | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
| 21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | 21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | 
| 22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 
| 23  * | 23  * | 
| 24  * Google Author(s): Behdad Esfahbod | 24  * Google Author(s): Behdad Esfahbod | 
| 25  */ | 25  */ | 
| 26 | 26 | 
| 27 #include "hb-ot-shape-complex-arabic-private.hh" | 27 #include "hb-ot-shape-complex-arabic-private.hh" | 
| 28 #include "hb-ot-shape-private.hh" | 28 #include "hb-ot-shape-private.hh" | 
| 29 | 29 | 
| 30 | 30 | 
|  | 31 #ifndef HB_DEBUG_ARABIC | 
|  | 32 #define HB_DEBUG_ARABIC (HB_DEBUG+0) | 
|  | 33 #endif | 
|  | 34 | 
|  | 35 | 
| 31 /* buffer var allocations */ | 36 /* buffer var allocations */ | 
| 32 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */ | 37 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */ | 
| 33 | 38 | 
|  | 39 #define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0 | 
|  | 40 | 
|  | 41 /* See: | 
|  | 42  * https://github.com/behdad/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff5
     7823d#commitcomment-14248516 */ | 
|  | 43 #define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \ | 
|  | 44         (FLAG_SAFE (gen_cat) & \ | 
|  | 45          (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \ | 
|  | 46           FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \ | 
|  | 47           /*FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |*/ \ | 
|  | 48           FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ | 
|  | 49           FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ | 
|  | 50           /*FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |*/ \ | 
|  | 51           /*FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |*/ \ | 
|  | 52           FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ | 
|  | 53           FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ | 
|  | 54           FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | \ | 
|  | 55           FLAG (HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | \ | 
|  | 56           FLAG (HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER) | \ | 
|  | 57           FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER) | \ | 
|  | 58           FLAG (HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL) | \ | 
|  | 59           FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL) | \ | 
|  | 60           FLAG (HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL) | \ | 
|  | 61           FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL))) | 
|  | 62 | 
| 34 | 63 | 
| 35 /* | 64 /* | 
| 36  * Joining types: | 65  * Joining types: | 
| 37  */ | 66  */ | 
| 38 | 67 | 
| 39 /* | 68 /* | 
| 40  * Bits used in the joining tables | 69  * Bits used in the joining tables | 
| 41  */ | 70  */ | 
| 42 enum hb_arabic_joining_type_t { | 71 enum hb_arabic_joining_type_t { | 
| 43   JOINING_TYPE_U                = 0, | 72   JOINING_TYPE_U                = 0, | 
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 77   HB_TAG('f','i','n','2'), | 106   HB_TAG('f','i','n','2'), | 
| 78   HB_TAG('f','i','n','3'), | 107   HB_TAG('f','i','n','3'), | 
| 79   HB_TAG('m','e','d','i'), | 108   HB_TAG('m','e','d','i'), | 
| 80   HB_TAG('m','e','d','2'), | 109   HB_TAG('m','e','d','2'), | 
| 81   HB_TAG('i','n','i','t'), | 110   HB_TAG('i','n','i','t'), | 
| 82   HB_TAG_NONE | 111   HB_TAG_NONE | 
| 83 }; | 112 }; | 
| 84 | 113 | 
| 85 | 114 | 
| 86 /* Same order as the feature array */ | 115 /* Same order as the feature array */ | 
| 87 enum { | 116 enum arabic_action_t { | 
| 88   ISOL, | 117   ISOL, | 
| 89   FINA, | 118   FINA, | 
| 90   FIN2, | 119   FIN2, | 
| 91   FIN3, | 120   FIN3, | 
| 92   MEDI, | 121   MEDI, | 
| 93   MED2, | 122   MED2, | 
| 94   INIT, | 123   INIT, | 
| 95 | 124 | 
| 96   NONE, | 125   NONE, | 
| 97 | 126 | 
| 98   ARABIC_NUM_FEATURES = NONE | 127   ARABIC_NUM_FEATURES = NONE, | 
|  | 128 | 
|  | 129   /* We abuse the same byte for other things... */ | 
|  | 130   STCH_FIXED, | 
|  | 131   STCH_REPEATING, | 
| 99 }; | 132 }; | 
| 100 | 133 | 
| 101 static const struct arabic_state_table_entry { | 134 static const struct arabic_state_table_entry { | 
| 102         uint8_t prev_action; | 135         uint8_t prev_action; | 
| 103         uint8_t curr_action; | 136         uint8_t curr_action; | 
| 104         uint16_t next_state; | 137         uint16_t next_state; | 
| 105 } arabic_state_table[][NUM_STATE_MACHINE_COLS] = | 138 } arabic_state_table[][NUM_STATE_MACHINE_COLS] = | 
| 106 { | 139 { | 
| 107   /*   jt_U,          jt_L,          jt_R,          jt_D,          jg_ALAPH,    
       jg_DALATH_RISH */ | 140   /*   jt_U,          jt_L,          jt_R,          jt_D,          jg_ALAPH,    
       jg_DALATH_RISH */ | 
| 108 | 141 | 
| (...skipping 24 matching lines...) Expand all  Loading... | 
| 133 nuke_joiners (const hb_ot_shape_plan_t *plan, | 166 nuke_joiners (const hb_ot_shape_plan_t *plan, | 
| 134               hb_font_t *font, | 167               hb_font_t *font, | 
| 135               hb_buffer_t *buffer); | 168               hb_buffer_t *buffer); | 
| 136 | 169 | 
| 137 static void | 170 static void | 
| 138 arabic_fallback_shape (const hb_ot_shape_plan_t *plan, | 171 arabic_fallback_shape (const hb_ot_shape_plan_t *plan, | 
| 139                        hb_font_t *font, | 172                        hb_font_t *font, | 
| 140                        hb_buffer_t *buffer); | 173                        hb_buffer_t *buffer); | 
| 141 | 174 | 
| 142 static void | 175 static void | 
|  | 176 record_stch (const hb_ot_shape_plan_t *plan, | 
|  | 177              hb_font_t *font, | 
|  | 178              hb_buffer_t *buffer); | 
|  | 179 | 
|  | 180 static void | 
| 143 collect_features_arabic (hb_ot_shape_planner_t *plan) | 181 collect_features_arabic (hb_ot_shape_planner_t *plan) | 
| 144 { | 182 { | 
| 145   hb_ot_map_builder_t *map = &plan->map; | 183   hb_ot_map_builder_t *map = &plan->map; | 
| 146 | 184 | 
| 147   /* We apply features according to the Arabic spec, with pauses | 185   /* We apply features according to the Arabic spec, with pauses | 
| 148    * in between most. | 186    * in between most. | 
| 149    * | 187    * | 
| 150    * The pause between init/medi/... and rlig is required.  See eg: | 188    * The pause between init/medi/... and rlig is required.  See eg: | 
| 151    * https://bugzilla.mozilla.org/show_bug.cgi?id=644184 | 189    * https://bugzilla.mozilla.org/show_bug.cgi?id=644184 | 
| 152    * | 190    * | 
| 153    * The pauses between init/medi/... themselves are not necessarily | 191    * The pauses between init/medi/... themselves are not necessarily | 
| 154    * needed as only one of those features is applied to any character. | 192    * needed as only one of those features is applied to any character. | 
| 155    * The only difference it makes is when fonts have contextual | 193    * The only difference it makes is when fonts have contextual | 
| 156    * substitutions.  We now follow the order of the spec, which makes | 194    * substitutions.  We now follow the order of the spec, which makes | 
| 157    * for better experience if that's what Uniscribe is doing. | 195    * for better experience if that's what Uniscribe is doing. | 
| 158    * | 196    * | 
| 159    * At least for Arabic, looks like Uniscribe has a pause between | 197    * At least for Arabic, looks like Uniscribe has a pause between | 
| 160    * rlig and calt.  Otherwise the IranNastaliq's ALLAH ligature won't | 198    * rlig and calt.  Otherwise the IranNastaliq's ALLAH ligature won't | 
| 161    * work.  However, testing shows that rlig and calt are applied | 199    * work.  However, testing shows that rlig and calt are applied | 
| 162    * together for Mongolian in Uniscribe.  As such, we only add a | 200    * together for Mongolian in Uniscribe.  As such, we only add a | 
| 163    * pause for Arabic, not other scripts. | 201    * pause for Arabic, not other scripts. | 
| 164    */ | 202    */ | 
| 165 | 203 | 
| 166   map->add_gsub_pause (nuke_joiners); | 204   map->add_gsub_pause (nuke_joiners); | 
| 167 | 205 | 
|  | 206   map->add_global_bool_feature (HB_TAG('s','t','c','h')); | 
|  | 207   map->add_gsub_pause (record_stch); | 
|  | 208 | 
| 168   map->add_global_bool_feature (HB_TAG('c','c','m','p')); | 209   map->add_global_bool_feature (HB_TAG('c','c','m','p')); | 
| 169   map->add_global_bool_feature (HB_TAG('l','o','c','l')); | 210   map->add_global_bool_feature (HB_TAG('l','o','c','l')); | 
| 170 | 211 | 
| 171   map->add_gsub_pause (NULL); | 212   map->add_gsub_pause (NULL); | 
| 172 | 213 | 
| 173   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) | 214   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) | 
| 174   { | 215   { | 
| 175     bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SY
     RIAC (arabic_features[i]); | 216     bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SY
     RIAC (arabic_features[i]); | 
| 176     map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_N
     ONE); | 217     map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_N
     ONE); | 
| 177     map->add_gsub_pause (NULL); | 218     map->add_gsub_pause (NULL); | 
| (...skipping 23 matching lines...) Expand all  Loading... | 
| 201 struct arabic_shape_plan_t | 242 struct arabic_shape_plan_t | 
| 202 { | 243 { | 
| 203   ASSERT_POD (); | 244   ASSERT_POD (); | 
| 204 | 245 | 
| 205   /* The "+ 1" in the next array is to accommodate for the "NONE" command, | 246   /* The "+ 1" in the next array is to accommodate for the "NONE" command, | 
| 206    * which is not an OpenType feature, but this simplifies the code by not | 247    * which is not an OpenType feature, but this simplifies the code by not | 
| 207    * having to do a "if (... < NONE) ..." and just rely on the fact that | 248    * having to do a "if (... < NONE) ..." and just rely on the fact that | 
| 208    * mask_array[NONE] == 0. */ | 249    * mask_array[NONE] == 0. */ | 
| 209   hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; | 250   hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; | 
| 210 | 251 | 
| 211   bool do_fallback; |  | 
| 212   arabic_fallback_plan_t *fallback_plan; | 252   arabic_fallback_plan_t *fallback_plan; | 
|  | 253 | 
|  | 254   unsigned int do_fallback : 1; | 
|  | 255   unsigned int has_stch : 1; | 
| 213 }; | 256 }; | 
| 214 | 257 | 
| 215 void * | 258 void * | 
| 216 data_create_arabic (const hb_ot_shape_plan_t *plan) | 259 data_create_arabic (const hb_ot_shape_plan_t *plan) | 
| 217 { | 260 { | 
| 218   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (
     arabic_shape_plan_t)); | 261   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (
     arabic_shape_plan_t)); | 
| 219   if (unlikely (!arabic_plan)) | 262   if (unlikely (!arabic_plan)) | 
| 220     return NULL; | 263     return NULL; | 
| 221 | 264 | 
| 222   arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; | 265   arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; | 
|  | 266   arabic_plan->has_stch = !!plan->map.get_1_mask (HB_TAG ('s','t','c','h')); | 
| 223   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { | 267   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { | 
| 224     arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); | 268     arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); | 
| 225     arabic_plan->do_fallback = arabic_plan->do_fallback && | 269     arabic_plan->do_fallback = arabic_plan->do_fallback && | 
| 226                                (FEATURE_IS_SYRIAC (arabic_features[i]) || | 270                                (FEATURE_IS_SYRIAC (arabic_features[i]) || | 
| 227                                 plan->map.needs_fallback (arabic_features[i])); | 271                                 plan->map.needs_fallback (arabic_features[i])); | 
| 228   } | 272   } | 
| 229 | 273 | 
| 230   return arabic_plan; | 274   return arabic_plan; | 
| 231 } | 275 } | 
| 232 | 276 | 
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 313   HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); | 357   HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); | 
| 314 | 358 | 
| 315   arabic_joining (buffer); | 359   arabic_joining (buffer); | 
| 316   if (script == HB_SCRIPT_MONGOLIAN) | 360   if (script == HB_SCRIPT_MONGOLIAN) | 
| 317     mongolian_variation_selectors (buffer); | 361     mongolian_variation_selectors (buffer); | 
| 318 | 362 | 
| 319   unsigned int count = buffer->len; | 363   unsigned int count = buffer->len; | 
| 320   hb_glyph_info_t *info = buffer->info; | 364   hb_glyph_info_t *info = buffer->info; | 
| 321   for (unsigned int i = 0; i < count; i++) | 365   for (unsigned int i = 0; i < count; i++) | 
| 322     info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()]; | 366     info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()]; | 
| 323 |  | 
| 324   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); |  | 
| 325 } | 367 } | 
| 326 | 368 | 
| 327 static void | 369 static void | 
| 328 setup_masks_arabic (const hb_ot_shape_plan_t *plan, | 370 setup_masks_arabic (const hb_ot_shape_plan_t *plan, | 
| 329                     hb_buffer_t              *buffer, | 371                     hb_buffer_t              *buffer, | 
| 330                     hb_font_t                *font HB_UNUSED) | 372                     hb_font_t                *font HB_UNUSED) | 
| 331 { | 373 { | 
| 332   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->d
     ata; | 374   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->d
     ata; | 
| 333   setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script); | 375   setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script); | 
| 334 } | 376 } | 
| (...skipping 29 matching lines...) Expand all  Loading... | 
| 364     fallback_plan = arabic_fallback_plan_create (plan, font); | 406     fallback_plan = arabic_fallback_plan_create (plan, font); | 
| 365     if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (a
     rabic_plan))->fallback_plan, NULL, fallback_plan))) { | 407     if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (a
     rabic_plan))->fallback_plan, NULL, fallback_plan))) { | 
| 366       arabic_fallback_plan_destroy (fallback_plan); | 408       arabic_fallback_plan_destroy (fallback_plan); | 
| 367       goto retry; | 409       goto retry; | 
| 368     } | 410     } | 
| 369   } | 411   } | 
| 370 | 412 | 
| 371   arabic_fallback_plan_shape (fallback_plan, font, buffer); | 413   arabic_fallback_plan_shape (fallback_plan, font, buffer); | 
| 372 } | 414 } | 
| 373 | 415 | 
|  | 416 /* | 
|  | 417  * Stretch feature: "stch". | 
|  | 418  * See example here: | 
|  | 419  * https://www.microsoft.com/typography/OpenTypeDev/syriac/intro.htm | 
|  | 420  * We implement this in a generic way, such that the Arabic subtending | 
|  | 421  * marks can use it as well. | 
|  | 422  */ | 
|  | 423 | 
|  | 424 static void | 
|  | 425 record_stch (const hb_ot_shape_plan_t *plan, | 
|  | 426              hb_font_t *font, | 
|  | 427              hb_buffer_t *buffer) | 
|  | 428 { | 
|  | 429   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->d
     ata; | 
|  | 430   if (!arabic_plan->has_stch) | 
|  | 431     return; | 
|  | 432 | 
|  | 433   /* 'stch' feature was just applied.  Look for anything that multiplied, | 
|  | 434    * and record it for stch treatment later.  Note that rtlm, frac, etc | 
|  | 435    * are applied before stch, but we assume that they didn't result in | 
|  | 436    * anything multiplying into 5 pieces, so it's safe-ish... */ | 
|  | 437 | 
|  | 438   unsigned int count = buffer->len; | 
|  | 439   hb_glyph_info_t *info = buffer->info; | 
|  | 440   for (unsigned int i = 0; i < count; i++) | 
|  | 441     if (unlikely (_hb_glyph_info_multiplied (&info[i]))) | 
|  | 442     { | 
|  | 443       unsigned int comp = _hb_glyph_info_get_lig_comp (&info[i]); | 
|  | 444       info[i].arabic_shaping_action() = comp % 2 ? STCH_REPEATING : STCH_FIXED; | 
|  | 445       buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH; | 
|  | 446     } | 
|  | 447 } | 
|  | 448 | 
|  | 449 static void | 
|  | 450 apply_stch (const hb_ot_shape_plan_t *plan, | 
|  | 451             hb_buffer_t              *buffer, | 
|  | 452             hb_font_t                *font) | 
|  | 453 { | 
|  | 454   if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH))
     ) | 
|  | 455     return; | 
|  | 456 | 
|  | 457   /* The Arabic shaper currently always processes in RTL mode, so we should | 
|  | 458    * stretch / position the stretched pieces to the left / preceding glyphs. */ | 
|  | 459 | 
|  | 460   /* We do a two pass implementation: | 
|  | 461    * First pass calculates the exact number of extra glyphs we need, | 
|  | 462    * We then enlarge buffer to have that much room, | 
|  | 463    * Second pass applies the stretch, copying things to the end of buffer. | 
|  | 464    */ | 
|  | 465 | 
|  | 466   /* 30 = 2048 / 70. | 
|  | 467    * https://www.microsoft.com/typography/cursivescriptguidelines.mspx */ | 
|  | 468   hb_position_t overlap = font->x_scale / 30; | 
|  | 469   DEBUG_MSG (ARABIC, NULL, "overlap for stretching is %d", overlap); | 
|  | 470   int sign = font->x_scale < 0 ? -1 : +1; | 
|  | 471   unsigned int extra_glyphs_needed = 0; // Set during MEASURE, used during CUT | 
|  | 472   typedef enum { MEASURE, CUT } step_t; | 
|  | 473 | 
|  | 474   for (step_t step = MEASURE; step <= CUT; step = (step_t) (step + 1)) | 
|  | 475   { | 
|  | 476     unsigned int count = buffer->len; | 
|  | 477     hb_glyph_info_t *info = buffer->info; | 
|  | 478     hb_glyph_position_t *pos = buffer->pos; | 
|  | 479     unsigned int new_len = count + extra_glyphs_needed; // write head during CUT | 
|  | 480     unsigned int j = new_len; | 
|  | 481     for (unsigned int i = count; i; i--) | 
|  | 482     { | 
|  | 483       if (!hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FIXE
     D, STCH_REPEATING)) | 
|  | 484       { | 
|  | 485         if (step == CUT) | 
|  | 486         { | 
|  | 487           --j; | 
|  | 488           info[j] = info[i - 1]; | 
|  | 489           pos[j] = pos[i - 1]; | 
|  | 490         } | 
|  | 491         continue; | 
|  | 492       } | 
|  | 493 | 
|  | 494       /* Yay, justification! */ | 
|  | 495 | 
|  | 496       hb_position_t w_total = 0; // Total to be filled | 
|  | 497       hb_position_t w_fixed = 0; // Sum of fixed tiles | 
|  | 498       hb_position_t w_repeating = 0; // Sum of repeating tiles | 
|  | 499       int n_fixed = 0; | 
|  | 500       int n_repeating = 0; | 
|  | 501 | 
|  | 502       unsigned int end = i; | 
|  | 503       while (i && | 
|  | 504              hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FI
     XED, STCH_REPEATING)) | 
|  | 505       { | 
|  | 506         i--; | 
|  | 507         hb_glyph_extents_t extents; | 
|  | 508         if (!font->get_glyph_extents (info[i].codepoint, &extents)) | 
|  | 509           extents.width = 0; | 
|  | 510         extents.width -= overlap; | 
|  | 511         if (info[i].arabic_shaping_action() == STCH_FIXED) | 
|  | 512         { | 
|  | 513           w_fixed += extents.width; | 
|  | 514           n_fixed++; | 
|  | 515         } | 
|  | 516         else | 
|  | 517         { | 
|  | 518           w_repeating += extents.width; | 
|  | 519           n_repeating++; | 
|  | 520         } | 
|  | 521       } | 
|  | 522       unsigned int start = i; | 
|  | 523       unsigned int context = i; | 
|  | 524       while (context && | 
|  | 525              !hb_in_range<unsigned> (info[context - 1].arabic_shaping_action(), 
     STCH_FIXED, STCH_REPEATING) && | 
|  | 526              (_hb_glyph_info_is_default_ignorable (&info[context - 1]) || | 
|  | 527               HB_ARABIC_GENERAL_CATEGORY_IS_WORD (_hb_glyph_info_get_general_cat
     egory (&info[context - 1])))) | 
|  | 528       { | 
|  | 529         context--; | 
|  | 530         w_total += pos[context].x_advance; | 
|  | 531       } | 
|  | 532       i++; // Don't touch i again. | 
|  | 533 | 
|  | 534       DEBUG_MSG (ARABIC, NULL, "%s stretch at (%d,%d,%d)", | 
|  | 535                  step == MEASURE ? "measuring" : "cutting", context, start, end)
     ; | 
|  | 536       DEBUG_MSG (ARABIC, NULL, "rest of word:    count=%d width %d", start - con
     text, w_total); | 
|  | 537       DEBUG_MSG (ARABIC, NULL, "fixed tiles:     count=%d width=%d", n_fixed, w_
     fixed); | 
|  | 538       DEBUG_MSG (ARABIC, NULL, "repeating tiles: count=%d width=%d", n_repeating
     , w_repeating); | 
|  | 539 | 
|  | 540       /* Number of additional times to repeat each repeating tile. */ | 
|  | 541       int n_copies = 0; | 
|  | 542 | 
|  | 543       hb_position_t w_remaining = w_total - w_fixed - overlap; | 
|  | 544       if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0) | 
|  | 545         n_copies = (sign * w_remaining + sign * w_repeating / 4) / (sign * w_rep
     eating) - 1; | 
|  | 546 | 
|  | 547       if (step == MEASURE) | 
|  | 548       { | 
|  | 549         extra_glyphs_needed += n_copies * n_repeating; | 
|  | 550         DEBUG_MSG (ARABIC, NULL, "will add extra %d copies of repeating tiles", 
     n_copies); | 
|  | 551       } | 
|  | 552       else | 
|  | 553       { | 
|  | 554         hb_position_t x_offset = -overlap; | 
|  | 555         for (unsigned int k = end; k > start; k--) | 
|  | 556         { | 
|  | 557           hb_glyph_extents_t extents; | 
|  | 558           if (!font->get_glyph_extents (info[k - 1].codepoint, &extents)) | 
|  | 559             extents.width = 0; | 
|  | 560           extents.width -= overlap; | 
|  | 561 | 
|  | 562           unsigned int repeat = 1; | 
|  | 563           if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) | 
|  | 564             repeat += n_copies; | 
|  | 565 | 
|  | 566           DEBUG_MSG (ARABIC, NULL, "appending %d copies of glyph %d; j=%d", | 
|  | 567                      repeat, info[k - 1].codepoint, j); | 
|  | 568           for (unsigned int n = 0; n < repeat; n++) | 
|  | 569           { | 
|  | 570             x_offset -= extents.width; | 
|  | 571             pos[k - 1].x_offset = x_offset; | 
|  | 572             /* Append copy. */ | 
|  | 573             --j; | 
|  | 574             info[j] = info[k - 1]; | 
|  | 575             pos[j] = pos[k - 1]; | 
|  | 576           } | 
|  | 577         } | 
|  | 578       } | 
|  | 579     } | 
|  | 580 | 
|  | 581     if (step == MEASURE) | 
|  | 582     { | 
|  | 583       if (unlikely (!buffer->ensure (count + extra_glyphs_needed))) | 
|  | 584         break; | 
|  | 585     } | 
|  | 586     else | 
|  | 587     { | 
|  | 588       assert (j == 0); | 
|  | 589       buffer->len = new_len; | 
|  | 590     } | 
|  | 591   } | 
|  | 592 } | 
|  | 593 | 
|  | 594 | 
|  | 595 static void | 
|  | 596 postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, | 
|  | 597                            hb_buffer_t              *buffer, | 
|  | 598                            hb_font_t                *font) | 
|  | 599 { | 
|  | 600   apply_stch (plan, buffer, font); | 
|  | 601 | 
|  | 602   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); | 
|  | 603 } | 
| 374 | 604 | 
| 375 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = | 605 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = | 
| 376 { | 606 { | 
| 377   "arabic", | 607   "arabic", | 
| 378   collect_features_arabic, | 608   collect_features_arabic, | 
| 379   NULL, /* override_features */ | 609   NULL, /* override_features */ | 
| 380   data_create_arabic, | 610   data_create_arabic, | 
| 381   data_destroy_arabic, | 611   data_destroy_arabic, | 
| 382   NULL, /* preprocess_text_arabic */ | 612   NULL, /* preprocess_text */ | 
|  | 613   postprocess_glyphs_arabic, | 
| 383   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, | 614   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, | 
| 384   NULL, /* decompose */ | 615   NULL, /* decompose */ | 
| 385   NULL, /* compose */ | 616   NULL, /* compose */ | 
| 386   setup_masks_arabic, | 617   setup_masks_arabic, | 
| 387   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, | 618   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, | 
| 388   true, /* fallback_position */ | 619   true, /* fallback_position */ | 
| 389 }; | 620 }; | 
| OLD | NEW | 
|---|