Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/harfbuzz/src/hb-ot-shape.cc @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* | |
| 2 * Copyright © 2009,2010 Red Hat, Inc. | |
| 3 * Copyright © 2010,2011,2012 Google, Inc. | |
| 4 * | |
| 5 * This is part of HarfBuzz, a text shaping library. | |
| 6 * | |
| 7 * Permission is hereby granted, without written agreement and without | |
| 8 * license or royalty fees, to use, copy, modify, and distribute this | |
| 9 * software and its documentation for any purpose, provided that the | |
| 10 * above copyright notice and the following two paragraphs appear in | |
| 11 * all copies of this software. | |
| 12 * | |
| 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | |
| 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | |
| 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | |
| 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |
| 17 * DAMAGE. | |
| 18 * | |
| 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | |
| 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
| 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | |
| 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | |
| 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
| 24 * | |
| 25 * Red Hat Author(s): Behdad Esfahbod | |
| 26 * Google Author(s): Behdad Esfahbod | |
| 27 */ | |
| 28 | |
| 29 #include "hb.hh" | |
| 30 | |
| 31 #ifndef HB_NO_OT_SHAPE | |
| 32 | |
| 33 #ifdef HB_NO_OT_LAYOUT | |
| 34 #error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT." | |
| 35 #endif | |
| 36 | |
| 37 #include "hb-shaper-impl.hh" | |
| 38 | |
| 39 #include "hb-ot-shape.hh" | |
| 40 #include "hb-ot-shaper.hh" | |
| 41 #include "hb-ot-shape-fallback.hh" | |
| 42 #include "hb-ot-shape-normalize.hh" | |
| 43 | |
| 44 #include "hb-ot-face.hh" | |
| 45 | |
| 46 #include "hb-set.hh" | |
| 47 | |
| 48 #include "hb-aat-layout.hh" | |
| 49 | |
| 50 static inline bool | |
| 51 _hb_codepoint_is_regional_indicator (hb_codepoint_t u) | |
| 52 { return hb_in_range<hb_codepoint_t> (u, 0x1F1E6u, 0x1F1FFu); } | |
| 53 | |
| 54 #ifndef HB_NO_AAT_SHAPE | |
| 55 static inline bool | |
| 56 _hb_apply_morx (hb_face_t *face, const hb_segment_properties_t &props) | |
| 57 { | |
| 58 /* https://github.com/harfbuzz/harfbuzz/issues/2124 */ | |
| 59 return hb_aat_layout_has_substitution (face) && | |
| 60 (HB_DIRECTION_IS_HORIZONTAL (props.direction) || !hb_ot_layout_has_substitution (face)); | |
| 61 } | |
| 62 #endif | |
| 63 | |
| 64 /** | |
| 65 * SECTION:hb-ot-shape | |
| 66 * @title: hb-ot-shape | |
| 67 * @short_description: OpenType shaping support | |
| 68 * @include: hb-ot.h | |
| 69 * | |
| 70 * Support functions for OpenType shaping related queries. | |
| 71 **/ | |
| 72 | |
| 73 | |
| 74 static void | |
| 75 hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, | |
| 76 const hb_feature_t *user_features, | |
| 77 unsigned int num_user_features); | |
| 78 | |
| 79 hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *face, | |
| 80 const hb_segment_properties_t &props) : | |
| 81 face (face), | |
| 82 props (props), | |
| 83 map (face, props), | |
| 84 aat_map (face, props) | |
| 85 #ifndef HB_NO_AAT_SHAPE | |
| 86 , apply_morx (_hb_apply_morx (face, props)) | |
| 87 #endif | |
| 88 { | |
| 89 shaper = hb_ot_shaper_categorize (this); | |
| 90 | |
| 91 script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; | |
| 92 script_fallback_mark_positioning = shaper->fallback_position; | |
| 93 | |
| 94 #ifndef HB_NO_AAT_SHAPE | |
| 95 /* https://github.com/harfbuzz/harfbuzz/issues/1528 */ | |
| 96 if (apply_morx && shaper != &_hb_ot_shaper_default) | |
| 97 shaper = &_hb_ot_shaper_dumber; | |
| 98 #endif | |
| 99 } | |
| 100 | |
| 101 void | |
| 102 hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, | |
| 103 const hb_ot_shape_plan_key_t &key) | |
| 104 { | |
| 105 plan.props = props; | |
| 106 plan.shaper = shaper; | |
| 107 map.compile (plan.map, key); | |
| 108 #ifndef HB_NO_AAT_SHAPE | |
| 109 if (apply_morx) | |
| 110 aat_map.compile (plan.aat_map); | |
| 111 #endif | |
| 112 | |
| 113 #ifndef HB_NO_OT_SHAPE_FRACTIONS | |
| 114 plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); | |
| 115 plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r')); | |
| 116 plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m')); | |
| 117 plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask); | |
| 118 #endif | |
| 119 | |
| 120 plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m')); | |
| 121 plan.has_vert = !!plan.map.get_1_mask (HB_TAG ('v','e','r','t')); | |
| 122 | |
| 123 hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ? | |
| 124 HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'); | |
| 125 #ifndef HB_NO_OT_KERN | |
| 126 plan.kern_mask = plan.map.get_mask (kern_tag); | |
| 127 plan.requested_kerning = !!plan.kern_mask; | |
| 128 #endif | |
| 129 #ifndef HB_NO_AAT_SHAPE | |
| 130 plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); | |
| 131 plan.requested_tracking = !!plan.trak_mask; | |
| 132 #endif | |
| 133 | |
| 134 bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; | |
| 135 bool disable_gpos = plan.shaper->gpos_tag && | |
| 136 plan.shaper->gpos_tag != plan.map.chosen_script[1]; | |
| 137 | |
| 138 /* | |
| 139 * Decide who provides glyph classes. GDEF or Unicode. | |
| 140 */ | |
| 141 | |
| 142 if (!hb_ot_layout_has_glyph_classes (face)) | |
| 143 plan.fallback_glyph_classes = true; | |
| 144 | |
| 145 /* | |
| 146 * Decide who does substitutions. GSUB, morx, or fallback. | |
| 147 */ | |
| 148 | |
| 149 #ifndef HB_NO_AAT_SHAPE | |
| 150 plan.apply_morx = apply_morx; | |
| 151 #endif | |
| 152 | |
| 153 /* | |
| 154 * Decide who does positioning. GPOS, kerx, kern, or fallback. | |
| 155 */ | |
| 156 | |
| 157 #ifndef HB_NO_AAT_SHAPE | |
| 158 bool has_kerx = hb_aat_layout_has_positioning (face); | |
| 159 bool has_gsub = !apply_morx && hb_ot_layout_has_substitution (face); | |
| 160 #endif | |
| 161 bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face); | |
| 162 if (false) | |
| 163 ; | |
| 164 #ifndef HB_NO_AAT_SHAPE | |
| 165 /* Prefer GPOS over kerx if GSUB is present; | |
| 166 * https://github.com/harfbuzz/harfbuzz/issues/3008 */ | |
| 167 else if (has_kerx && !(has_gsub && has_gpos)) | |
| 168 plan.apply_kerx = true; | |
| 169 #endif | |
| 170 else if (has_gpos) | |
| 171 plan.apply_gpos = true; | |
| 172 | |
| 173 if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) | |
| 174 { | |
| 175 #ifndef HB_NO_AAT_SHAPE | |
| 176 if (has_kerx) | |
| 177 plan.apply_kerx = true; | |
| 178 else | |
| 179 #endif | |
| 180 #ifndef HB_NO_OT_KERN | |
| 181 if (hb_ot_layout_has_kerning (face)) | |
| 182 plan.apply_kern = true; | |
| 183 #endif | |
| 184 } | |
| 185 | |
| 186 plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); | |
| 187 | |
| 188 plan.zero_marks = script_zero_marks && | |
| 189 !plan.apply_kerx && | |
| 190 (!plan.apply_kern | |
| 191 #ifndef HB_NO_OT_KERN | |
| 192 || !hb_ot_layout_has_machine_kerning (face) | |
| 193 #endif | |
| 194 ); | |
| 195 plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); | |
| 196 | |
| 197 plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos && | |
| 198 !plan.apply_kerx && | |
| 199 (!plan.apply_kern | |
| 200 #ifndef HB_NO_OT_KERN | |
| 201 || !hb_ot_layout_has_cross_kerning (face) | |
| 202 #endif | |
| 203 ); | |
| 204 | |
| 205 plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && | |
| 206 script_fallback_mark_positioning; | |
| 207 | |
| 208 #ifndef HB_NO_AAT_SHAPE | |
| 209 /* If we're using morx shaping, we cancel mark position adjustment because | |
| 210 Apple Color Emoji assumes this will NOT be done when forming emoji sequences; | |
| 211 https://github.com/harfbuzz/harfbuzz/issues/2967. */ | |
| 212 if (plan.apply_morx) | |
| 213 plan.adjust_mark_positioning_when_zeroing = false; | |
| 214 | |
| 215 /* Currently we always apply trak. */ | |
| 216 plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face); | |
| 217 #endif | |
| 218 } | |
| 219 | |
| 220 bool | |
| 221 hb_ot_shape_plan_t::init0 (hb_face_t *face, | |
| 222 const hb_shape_plan_key_t *key) | |
| 223 { | |
| 224 map.init (); | |
| 225 #ifndef HB_NO_AAT_SHAPE | |
| 226 aat_map.init (); | |
| 227 #endif | |
| 228 | |
| 229 hb_ot_shape_planner_t planner (face, | |
| 230 key->props); | |
| 231 | |
| 232 hb_ot_shape_collect_features (&planner, | |
| 233 key->user_features, | |
| 234 key->num_user_features); | |
| 235 | |
| 236 planner.compile (*this, key->ot); | |
| 237 | |
| 238 if (shaper->data_create) | |
| 239 { | |
| 240 data = shaper->data_create (this); | |
| 241 if (unlikely (!data)) | |
| 242 { | |
| 243 map.fini (); | |
| 244 #ifndef HB_NO_AAT_SHAPE | |
| 245 aat_map.fini (); | |
| 246 #endif | |
| 247 return false; | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 return true; | |
| 252 } | |
| 253 | |
| 254 void | |
| 255 hb_ot_shape_plan_t::fini () | |
| 256 { | |
| 257 if (shaper->data_destroy) | |
| 258 shaper->data_destroy (const_cast<void *> (data)); | |
| 259 | |
| 260 map.fini (); | |
| 261 #ifndef HB_NO_AAT_SHAPE | |
| 262 aat_map.fini (); | |
| 263 #endif | |
| 264 } | |
| 265 | |
| 266 void | |
| 267 hb_ot_shape_plan_t::substitute (hb_font_t *font, | |
| 268 hb_buffer_t *buffer) const | |
| 269 { | |
| 270 #ifndef HB_NO_AAT_SHAPE | |
| 271 if (unlikely (apply_morx)) | |
| 272 hb_aat_layout_substitute (this, font, buffer); | |
| 273 else | |
| 274 #endif | |
| 275 map.substitute (this, font, buffer); | |
| 276 } | |
| 277 | |
| 278 void | |
| 279 hb_ot_shape_plan_t::position (hb_font_t *font, | |
| 280 hb_buffer_t *buffer) const | |
| 281 { | |
| 282 if (this->apply_gpos) | |
| 283 map.position (this, font, buffer); | |
| 284 #ifndef HB_NO_AAT_SHAPE | |
| 285 else if (this->apply_kerx) | |
| 286 hb_aat_layout_position (this, font, buffer); | |
| 287 #endif | |
| 288 | |
| 289 #ifndef HB_NO_OT_KERN | |
| 290 if (this->apply_kern) | |
| 291 hb_ot_layout_kern (this, font, buffer); | |
| 292 #endif | |
| 293 else if (this->apply_fallback_kern) | |
| 294 _hb_ot_shape_fallback_kern (this, font, buffer); | |
| 295 | |
| 296 #ifndef HB_NO_AAT_SHAPE | |
| 297 if (this->apply_trak) | |
| 298 hb_aat_layout_track (this, font, buffer); | |
| 299 #endif | |
| 300 } | |
| 301 | |
| 302 | |
| 303 static const hb_ot_map_feature_t | |
| 304 common_features[] = | |
| 305 { | |
| 306 {HB_TAG('a','b','v','m'), F_GLOBAL}, | |
| 307 {HB_TAG('b','l','w','m'), F_GLOBAL}, | |
| 308 {HB_TAG('c','c','m','p'), F_GLOBAL}, | |
| 309 {HB_TAG('l','o','c','l'), F_GLOBAL}, | |
| 310 {HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS}, | |
| 311 {HB_TAG('m','k','m','k'), F_GLOBAL_MANUAL_JOINERS}, | |
| 312 {HB_TAG('r','l','i','g'), F_GLOBAL}, | |
| 313 }; | |
| 314 | |
| 315 | |
| 316 static const hb_ot_map_feature_t | |
| 317 horizontal_features[] = | |
| 318 { | |
| 319 {HB_TAG('c','a','l','t'), F_GLOBAL}, | |
| 320 {HB_TAG('c','l','i','g'), F_GLOBAL}, | |
| 321 {HB_TAG('c','u','r','s'), F_GLOBAL}, | |
| 322 {HB_TAG('d','i','s','t'), F_GLOBAL}, | |
| 323 {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK}, | |
| 324 {HB_TAG('l','i','g','a'), F_GLOBAL}, | |
| 325 {HB_TAG('r','c','l','t'), F_GLOBAL}, | |
| 326 }; | |
| 327 | |
| 328 static void | |
| 329 hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, | |
| 330 const hb_feature_t *user_features, | |
| 331 unsigned int num_user_features) | |
| 332 { | |
| 333 hb_ot_map_builder_t *map = &planner->map; | |
| 334 | |
| 335 map->enable_feature (HB_TAG('r','v','r','n')); | |
| 336 map->add_gsub_pause (nullptr); | |
| 337 | |
| 338 switch (planner->props.direction) | |
| 339 { | |
| 340 case HB_DIRECTION_LTR: | |
| 341 map->enable_feature (HB_TAG ('l','t','r','a')); | |
| 342 map->enable_feature (HB_TAG ('l','t','r','m')); | |
| 343 break; | |
| 344 case HB_DIRECTION_RTL: | |
| 345 map->enable_feature (HB_TAG ('r','t','l','a')); | |
| 346 map->add_feature (HB_TAG ('r','t','l','m')); | |
| 347 break; | |
| 348 case HB_DIRECTION_TTB: | |
| 349 case HB_DIRECTION_BTT: | |
| 350 case HB_DIRECTION_INVALID: | |
| 351 default: | |
| 352 break; | |
| 353 } | |
| 354 | |
| 355 #ifndef HB_NO_OT_SHAPE_FRACTIONS | |
| 356 /* Automatic fractions. */ | |
| 357 map->add_feature (HB_TAG ('f','r','a','c')); | |
| 358 map->add_feature (HB_TAG ('n','u','m','r')); | |
| 359 map->add_feature (HB_TAG ('d','n','o','m')); | |
| 360 #endif | |
| 361 | |
| 362 /* Random! */ | |
| 363 map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); | |
| 364 | |
| 365 #ifndef HB_NO_AAT_SHAPE | |
| 366 /* Tracking. We enable dummy feature here just to allow disabling | |
| 367 * AAT 'trak' table using features. | |
| 368 * https://github.com/harfbuzz/harfbuzz/issues/1303 */ | |
| 369 map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK); | |
| 370 #endif | |
| 371 | |
| 372 map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */ | |
| 373 map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */ | |
| 374 | |
| 375 if (planner->shaper->collect_features) | |
| 376 planner->shaper->collect_features (planner); | |
| 377 | |
| 378 map->enable_feature (HB_TAG ('B','u','z','z')); /* Considered required. */ | |
| 379 map->enable_feature (HB_TAG ('B','U','Z','Z')); /* Considered discretionary. */ | |
| 380 | |
| 381 for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++) | |
| 382 map->add_feature (common_features[i]); | |
| 383 | |
| 384 if (HB_DIRECTION_IS_HORIZONTAL (planner->props.direction)) | |
| 385 for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++) | |
| 386 map->add_feature (horizontal_features[i]); | |
| 387 else | |
| 388 { | |
| 389 /* We only apply `vert` feature. See: | |
| 390 * https://github.com/harfbuzz/harfbuzz/commit/d71c0df2d17f4590d5611239577a6cb532c26528 | |
| 391 * https://lists.freedesktop.org/archives/harfbuzz/2013-August/003490.html */ | |
| 392 | |
| 393 /* We really want to find a 'vert' feature if there's any in the font, no | |
| 394 * matter which script/langsys it is listed (or not) under. | |
| 395 * See various bugs referenced from: | |
| 396 * https://github.com/harfbuzz/harfbuzz/issues/63 */ | |
| 397 map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH); | |
| 398 } | |
| 399 | |
| 400 for (unsigned int i = 0; i < num_user_features; i++) | |
| 401 { | |
| 402 const hb_feature_t *feature = &user_features[i]; | |
| 403 map->add_feature (feature->tag, | |
| 404 (feature->start == HB_FEATURE_GLOBAL_START && | |
| 405 feature->end == HB_FEATURE_GLOBAL_END) ? F_GLOBAL : F_NONE, | |
| 406 feature->value); | |
| 407 } | |
| 408 | |
| 409 #ifndef HB_NO_AAT_SHAPE | |
| 410 if (planner->apply_morx) | |
| 411 { | |
| 412 hb_aat_map_builder_t *aat_map = &planner->aat_map; | |
| 413 for (unsigned int i = 0; i < num_user_features; i++) | |
| 414 { | |
| 415 const hb_feature_t *feature = &user_features[i]; | |
| 416 aat_map->add_feature (feature->tag, feature->value); | |
| 417 } | |
| 418 } | |
| 419 #endif | |
| 420 | |
| 421 if (planner->shaper->override_features) | |
| 422 planner->shaper->override_features (planner); | |
| 423 } | |
| 424 | |
| 425 | |
| 426 /* | |
| 427 * shaper face data | |
| 428 */ | |
| 429 | |
| 430 struct hb_ot_face_data_t {}; | |
| 431 | |
| 432 hb_ot_face_data_t * | |
| 433 _hb_ot_shaper_face_data_create (hb_face_t *face) | |
| 434 { | |
| 435 return (hb_ot_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; | |
| 436 } | |
| 437 | |
| 438 void | |
| 439 _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) | |
| 440 { | |
| 441 } | |
| 442 | |
| 443 | |
| 444 /* | |
| 445 * shaper font data | |
| 446 */ | |
| 447 | |
| 448 struct hb_ot_font_data_t {}; | |
| 449 | |
| 450 hb_ot_font_data_t * | |
| 451 _hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED) | |
| 452 { | |
| 453 return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; | |
| 454 } | |
| 455 | |
| 456 void | |
| 457 _hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED) | |
| 458 { | |
| 459 } | |
| 460 | |
| 461 | |
| 462 /* | |
| 463 * shaper | |
| 464 */ | |
| 465 | |
| 466 struct hb_ot_shape_context_t | |
| 467 { | |
| 468 hb_ot_shape_plan_t *plan; | |
| 469 hb_font_t *font; | |
| 470 hb_face_t *face; | |
| 471 hb_buffer_t *buffer; | |
| 472 const hb_feature_t *user_features; | |
| 473 unsigned int num_user_features; | |
| 474 | |
| 475 /* Transient stuff */ | |
| 476 hb_direction_t target_direction; | |
| 477 }; | |
| 478 | |
| 479 | |
| 480 | |
| 481 /* Main shaper */ | |
| 482 | |
| 483 | |
| 484 /* Prepare */ | |
| 485 | |
| 486 static void | |
| 487 hb_set_unicode_props (hb_buffer_t *buffer) | |
| 488 { | |
| 489 /* Implement enough of Unicode Graphemes here that shaping | |
| 490 * in reverse-direction wouldn't break graphemes. Namely, | |
| 491 * we mark all marks and ZWJ and ZWJ,Extended_Pictographic | |
| 492 * sequences as continuations. The foreach_grapheme() | |
| 493 * macro uses this bit. | |
| 494 * | |
| 495 * https://www.unicode.org/reports/tr29/#Regex_Definitions | |
| 496 */ | |
| 497 unsigned int count = buffer->len; | |
| 498 hb_glyph_info_t *info = buffer->info; | |
| 499 for (unsigned int i = 0; i < count; i++) | |
| 500 { | |
| 501 _hb_glyph_info_set_unicode_props (&info[i], buffer); | |
| 502 | |
| 503 /* Marks are already set as continuation by the above line. | |
| 504 * Handle Emoji_Modifier and ZWJ-continuation. */ | |
| 505 if (unlikely (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL && | |
| 506 hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x1F3FBu, 0x1F3FFu))) | |
| 507 { | |
| 508 _hb_glyph_info_set_continuation (&info[i]); | |
| 509 } | |
| 510 /* Regional_Indicators are hairy as hell... | |
| 511 * https://github.com/harfbuzz/harfbuzz/issues/2265 */ | |
| 512 else if (unlikely (i && _hb_codepoint_is_regional_indicator (info[i].codepoint))) | |
| 513 { | |
| 514 if (_hb_codepoint_is_regional_indicator (info[i - 1].codepoint) && | |
| 515 !_hb_glyph_info_is_continuation (&info[i - 1])) | |
| 516 _hb_glyph_info_set_continuation (&info[i]); | |
| 517 } | |
| 518 #ifndef HB_NO_EMOJI_SEQUENCES | |
| 519 else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) | |
| 520 { | |
| 521 _hb_glyph_info_set_continuation (&info[i]); | |
| 522 if (i + 1 < count && | |
| 523 _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint)) | |
| 524 { | |
| 525 i++; | |
| 526 _hb_glyph_info_set_unicode_props (&info[i], buffer); | |
| 527 _hb_glyph_info_set_continuation (&info[i]); | |
| 528 } | |
| 529 } | |
| 530 #endif | |
| 531 /* Or part of the Other_Grapheme_Extend that is not marks. | |
| 532 * As of Unicode 15 that is just: | |
| 533 * | |
| 534 * 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER | |
| 535 * FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK | |
| 536 * E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG | |
| 537 * | |
| 538 * ZWNJ is special, we don't want to merge it as there's no need, and keeping | |
| 539 * it separate results in more granular clusters. | |
| 540 * Tags are used for Emoji sub-region flag sequences: | |
| 541 * https://github.com/harfbuzz/harfbuzz/issues/1556 | |
| 542 * Katakana ones were requested: | |
| 543 * https://github.com/harfbuzz/harfbuzz/issues/3844 | |
| 544 */ | |
| 545 else if (unlikely (hb_in_ranges<hb_codepoint_t> (info[i].codepoint, 0xFF9Eu, 0xFF9Fu, 0xE0020u, 0xE007Fu))) | |
| 546 _hb_glyph_info_set_continuation (&info[i]); | |
| 547 } | |
| 548 } | |
| 549 | |
| 550 static void | |
| 551 hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) | |
| 552 { | |
| 553 if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) | |
| 554 return; | |
| 555 | |
| 556 if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || | |
| 557 buffer->context_len[0] || | |
| 558 !_hb_glyph_info_is_unicode_mark (&buffer->info[0])) | |
| 559 return; | |
| 560 | |
| 561 if (!font->has_glyph (0x25CCu)) | |
| 562 return; | |
| 563 | |
| 564 hb_glyph_info_t dottedcircle = {0}; | |
| 565 dottedcircle.codepoint = 0x25CCu; | |
| 566 _hb_glyph_info_set_unicode_props (&dottedcircle, buffer); | |
| 567 | |
| 568 buffer->clear_output (); | |
| 569 | |
| 570 buffer->idx = 0; | |
| 571 hb_glyph_info_t info = dottedcircle; | |
| 572 info.cluster = buffer->cur().cluster; | |
| 573 info.mask = buffer->cur().mask; | |
| 574 (void) buffer->output_info (info); | |
| 575 | |
| 576 buffer->sync (); | |
| 577 } | |
| 578 | |
| 579 static void | |
| 580 hb_form_clusters (hb_buffer_t *buffer) | |
| 581 { | |
| 582 if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) | |
| 583 return; | |
| 584 | |
| 585 if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) | |
| 586 foreach_grapheme (buffer, start, end) | |
| 587 buffer->merge_clusters (start, end); | |
| 588 else | |
| 589 foreach_grapheme (buffer, start, end) | |
| 590 buffer->unsafe_to_break (start, end); | |
| 591 } | |
| 592 | |
| 593 static void | |
| 594 hb_ensure_native_direction (hb_buffer_t *buffer) | |
| 595 { | |
| 596 hb_direction_t direction = buffer->props.direction; | |
| 597 hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script); | |
| 598 | |
| 599 /* Numeric runs in natively-RTL scripts are actually native-LTR, so we reset | |
| 600 * the horiz_dir if the run contains at least one decimal-number char, and no | |
| 601 * letter chars (ideally we should be checking for chars with strong | |
| 602 * directionality but hb-unicode currently lacks bidi categories). | |
| 603 * | |
| 604 * This allows digit sequences in Arabic etc to be shaped in "native" | |
| 605 * direction, so that features like ligatures will work as intended. | |
| 606 * | |
| 607 * https://github.com/harfbuzz/harfbuzz/issues/501 | |
| 608 * | |
| 609 * Similar thing about Regional_Indicators; They are bidi=L, but Script=Common. | |
| 610 * If they are present in a run of natively-RTL text, they get assigned a script | |
| 611 * with natively RTL direction, which would result in wrong shaping if we | |
| 612 * assign such native RTL direction to them then. Detect that as well. | |
| 613 * | |
| 614 * https://github.com/harfbuzz/harfbuzz/issues/3314 | |
| 615 */ | |
| 616 if (unlikely (horiz_dir == HB_DIRECTION_RTL && direction == HB_DIRECTION_LTR)) | |
| 617 { | |
| 618 bool found_number = false, found_letter = false, found_ri = false; | |
| 619 const auto* info = buffer->info; | |
| 620 const auto count = buffer->len; | |
| 621 for (unsigned i = 0; i < count; i++) | |
| 622 { | |
| 623 auto gc = _hb_glyph_info_get_general_category (&info[i]); | |
| 624 if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | |
| 625 found_number = true; | |
| 626 else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc)) | |
| 627 { | |
| 628 found_letter = true; | |
| 629 break; | |
| 630 } | |
| 631 else if (_hb_codepoint_is_regional_indicator (info[i].codepoint)) | |
| 632 found_ri = true; | |
| 633 } | |
| 634 if ((found_number || found_ri) && !found_letter) | |
| 635 horiz_dir = HB_DIRECTION_LTR; | |
| 636 } | |
| 637 | |
| 638 /* TODO vertical: | |
| 639 * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType | |
| 640 * Ogham fonts are supposed to be implemented BTT or not. Need to research that | |
| 641 * first. */ | |
| 642 if ((HB_DIRECTION_IS_HORIZONTAL (direction) && | |
| 643 direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) || | |
| 644 (HB_DIRECTION_IS_VERTICAL (direction) && | |
| 645 direction != HB_DIRECTION_TTB)) | |
| 646 { | |
| 647 _hb_ot_layout_reverse_graphemes (buffer); | |
| 648 buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); | |
| 649 } | |
| 650 } | |
| 651 | |
| 652 | |
| 653 /* | |
| 654 * Substitute | |
| 655 */ | |
| 656 | |
| 657 #ifndef HB_NO_VERTICAL | |
| 658 static hb_codepoint_t | |
| 659 hb_vert_char_for (hb_codepoint_t u) | |
| 660 { | |
| 661 switch (u >> 8) | |
| 662 { | |
| 663 case 0x20: switch (u) { | |
| 664 case 0x2013u: return 0xfe32u; // EN DASH | |
| 665 case 0x2014u: return 0xfe31u; // EM DASH | |
| 666 case 0x2025u: return 0xfe30u; // TWO DOT LEADER | |
| 667 case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS | |
| 668 } break; | |
| 669 case 0x30: switch (u) { | |
| 670 case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA | |
| 671 case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP | |
| 672 case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET | |
| 673 case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET | |
| 674 case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET | |
| 675 case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET | |
| 676 case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET | |
| 677 case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET | |
| 678 case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET | |
| 679 case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET | |
| 680 case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET | |
| 681 case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET | |
| 682 case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET | |
| 683 case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET | |
| 684 case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET | |
| 685 case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET | |
| 686 } break; | |
| 687 case 0xfe: switch (u) { | |
| 688 case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE | |
| 689 } break; | |
| 690 case 0xff: switch (u) { | |
| 691 case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK | |
| 692 case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS | |
| 693 case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS | |
| 694 case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA | |
| 695 case 0xff1au: return 0xfe13u; // FULLWIDTH COLON | |
| 696 case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON | |
| 697 case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK | |
| 698 case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET | |
| 699 case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET | |
| 700 case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE | |
| 701 case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET | |
| 702 case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET | |
| 703 } break; | |
| 704 } | |
| 705 | |
| 706 return u; | |
| 707 } | |
| 708 #endif | |
| 709 | |
| 710 static inline void | |
| 711 hb_ot_rotate_chars (const hb_ot_shape_context_t *c) | |
| 712 { | |
| 713 hb_buffer_t *buffer = c->buffer; | |
| 714 unsigned int count = buffer->len; | |
| 715 hb_glyph_info_t *info = buffer->info; | |
| 716 | |
| 717 if (HB_DIRECTION_IS_BACKWARD (c->target_direction)) | |
| 718 { | |
| 719 hb_unicode_funcs_t *unicode = buffer->unicode; | |
| 720 hb_mask_t rtlm_mask = c->plan->rtlm_mask; | |
| 721 | |
| 722 for (unsigned int i = 0; i < count; i++) { | |
| 723 hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); | |
| 724 if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) | |
| 725 info[i].codepoint = codepoint; | |
| 726 else | |
| 727 info[i].mask |= rtlm_mask; | |
| 728 } | |
| 729 } | |
| 730 | |
| 731 #ifndef HB_NO_VERTICAL | |
| 732 if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert) | |
| 733 { | |
| 734 for (unsigned int i = 0; i < count; i++) { | |
| 735 hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint); | |
| 736 if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) | |
| 737 info[i].codepoint = codepoint; | |
| 738 } | |
| 739 } | |
| 740 #endif | |
| 741 } | |
| 742 | |
| 743 static inline void | |
| 744 hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) | |
| 745 { | |
| 746 #ifdef HB_NO_OT_SHAPE_FRACTIONS | |
| 747 return; | |
| 748 #endif | |
| 749 | |
| 750 if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || | |
| 751 !c->plan->has_frac) | |
| 752 return; | |
| 753 | |
| 754 hb_buffer_t *buffer = c->buffer; | |
| 755 | |
| 756 hb_mask_t pre_mask, post_mask; | |
| 757 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) | |
| 758 { | |
| 759 pre_mask = c->plan->numr_mask | c->plan->frac_mask; | |
| 760 post_mask = c->plan->frac_mask | c->plan->dnom_mask; | |
| 761 } | |
| 762 else | |
| 763 { | |
| 764 pre_mask = c->plan->frac_mask | c->plan->dnom_mask; | |
| 765 post_mask = c->plan->numr_mask | c->plan->frac_mask; | |
| 766 } | |
| 767 | |
| 768 unsigned int count = buffer->len; | |
| 769 hb_glyph_info_t *info = buffer->info; | |
| 770 for (unsigned int i = 0; i < count; i++) | |
| 771 { | |
| 772 if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */ | |
| 773 { | |
| 774 unsigned int start = i, end = i + 1; | |
| 775 while (start && | |
| 776 _hb_glyph_info_get_general_category (&info[start - 1]) == | |
| 777 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | |
| 778 start--; | |
| 779 while (end < count && | |
| 780 _hb_glyph_info_get_general_category (&info[end]) == | |
| 781 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | |
| 782 end++; | |
| 783 | |
| 784 buffer->unsafe_to_break (start, end); | |
| 785 | |
| 786 for (unsigned int j = start; j < i; j++) | |
| 787 info[j].mask |= pre_mask; | |
| 788 info[i].mask |= c->plan->frac_mask; | |
| 789 for (unsigned int j = i + 1; j < end; j++) | |
| 790 info[j].mask |= post_mask; | |
| 791 | |
| 792 i = end - 1; | |
| 793 } | |
| 794 } | |
| 795 } | |
| 796 | |
| 797 static inline void | |
| 798 hb_ot_shape_initialize_masks (const hb_ot_shape_context_t *c) | |
| 799 { | |
| 800 hb_ot_map_t *map = &c->plan->map; | |
| 801 hb_buffer_t *buffer = c->buffer; | |
| 802 | |
| 803 hb_mask_t global_mask = map->get_global_mask (); | |
| 804 buffer->reset_masks (global_mask); | |
| 805 } | |
| 806 | |
| 807 static inline void | |
| 808 hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c) | |
| 809 { | |
| 810 hb_ot_map_t *map = &c->plan->map; | |
| 811 hb_buffer_t *buffer = c->buffer; | |
| 812 | |
| 813 hb_ot_shape_setup_masks_fraction (c); | |
| 814 | |
| 815 if (c->plan->shaper->setup_masks) | |
| 816 c->plan->shaper->setup_masks (c->plan, buffer, c->font); | |
| 817 | |
| 818 for (unsigned int i = 0; i < c->num_user_features; i++) | |
| 819 { | |
| 820 const hb_feature_t *feature = &c->user_features[i]; | |
| 821 if (!(feature->start == HB_FEATURE_GLOBAL_START && feature->end == HB_FEATURE_GLOBAL_END)) { | |
| 822 unsigned int shift; | |
| 823 hb_mask_t mask = map->get_mask (feature->tag, &shift); | |
| 824 buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); | |
| 825 } | |
| 826 } | |
| 827 } | |
| 828 | |
| 829 static void | |
| 830 hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer) | |
| 831 { | |
| 832 if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || | |
| 833 (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) || | |
| 834 (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES)) | |
| 835 return; | |
| 836 | |
| 837 unsigned int count = buffer->len; | |
| 838 hb_glyph_info_t *info = buffer->info; | |
| 839 hb_glyph_position_t *pos = buffer->pos; | |
| 840 unsigned int i = 0; | |
| 841 for (i = 0; i < count; i++) | |
| 842 if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) | |
| 843 pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; | |
| 844 } | |
| 845 | |
| 846 static void | |
| 847 hb_ot_hide_default_ignorables (hb_buffer_t *buffer, | |
| 848 hb_font_t *font) | |
| 849 { | |
| 850 if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || | |
| 851 (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) | |
| 852 return; | |
| 853 | |
| 854 unsigned int count = buffer->len; | |
| 855 hb_glyph_info_t *info = buffer->info; | |
| 856 | |
| 857 hb_codepoint_t invisible = buffer->invisible; | |
| 858 if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) && | |
| 859 (invisible || font->get_nominal_glyph (' ', &invisible))) | |
| 860 { | |
| 861 /* Replace default-ignorables with a zero-advance invisible glyph. */ | |
| 862 for (unsigned int i = 0; i < count; i++) | |
| 863 { | |
| 864 if (_hb_glyph_info_is_default_ignorable (&info[i])) | |
| 865 info[i].codepoint = invisible; | |
| 866 } | |
| 867 } | |
| 868 else | |
| 869 buffer->delete_glyphs_inplace (_hb_glyph_info_is_default_ignorable); | |
| 870 } | |
| 871 | |
| 872 | |
| 873 static inline void | |
| 874 hb_ot_map_glyphs_fast (hb_buffer_t *buffer) | |
| 875 { | |
| 876 /* Normalization process sets up glyph_index(), we just copy it. */ | |
| 877 unsigned int count = buffer->len; | |
| 878 hb_glyph_info_t *info = buffer->info; | |
| 879 for (unsigned int i = 0; i < count; i++) | |
| 880 info[i].codepoint = info[i].glyph_index(); | |
| 881 | |
| 882 buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; | |
| 883 } | |
| 884 | |
| 885 static inline void | |
| 886 hb_synthesize_glyph_classes (hb_buffer_t *buffer) | |
| 887 { | |
| 888 unsigned int count = buffer->len; | |
| 889 hb_glyph_info_t *info = buffer->info; | |
| 890 for (unsigned int i = 0; i < count; i++) | |
| 891 { | |
| 892 hb_ot_layout_glyph_props_flags_t klass; | |
| 893 | |
| 894 /* Never mark default-ignorables as marks. | |
| 895 * They won't get in the way of lookups anyway, | |
| 896 * but having them as mark will cause them to be skipped | |
| 897 * over if the lookup-flag says so, but at least for the | |
| 898 * Mongolian variation selectors, looks like Uniscribe | |
| 899 * marks them as non-mark. Some Mongolian fonts without | |
| 900 * GDEF rely on this. Another notable character that | |
| 901 * this applies to is COMBINING GRAPHEME JOINER. */ | |
| 902 klass = (_hb_glyph_info_get_general_category (&info[i]) != | |
| 903 HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK || | |
| 904 _hb_glyph_info_is_default_ignorable (&info[i])) ? | |
| 905 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : | |
| 906 HB_OT_LAYOUT_GLYPH_PROPS_MARK; | |
| 907 _hb_glyph_info_set_glyph_props (&info[i], klass); | |
| 908 } | |
| 909 } | |
| 910 | |
| 911 static inline void | |
| 912 hb_ot_substitute_default (const hb_ot_shape_context_t *c) | |
| 913 { | |
| 914 hb_buffer_t *buffer = c->buffer; | |
| 915 | |
| 916 hb_ot_rotate_chars (c); | |
| 917 | |
| 918 HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); | |
| 919 | |
| 920 _hb_ot_shape_normalize (c->plan, buffer, c->font); | |
| 921 | |
| 922 hb_ot_shape_setup_masks (c); | |
| 923 | |
| 924 /* This is unfortunate to go here, but necessary... */ | |
| 925 if (c->plan->fallback_mark_positioning) | |
| 926 _hb_ot_shape_fallback_mark_position_recategorize_marks (c->plan, c->font, buffer); | |
| 927 | |
| 928 hb_ot_map_glyphs_fast (buffer); | |
| 929 | |
| 930 HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); | |
| 931 } | |
| 932 | |
| 933 static inline void | |
| 934 hb_ot_substitute_plan (const hb_ot_shape_context_t *c) | |
| 935 { | |
| 936 hb_buffer_t *buffer = c->buffer; | |
| 937 | |
| 938 hb_ot_layout_substitute_start (c->font, buffer); | |
| 939 | |
| 940 if (c->plan->fallback_glyph_classes) | |
| 941 hb_synthesize_glyph_classes (c->buffer); | |
| 942 | |
| 943 c->plan->substitute (c->font, buffer); | |
| 944 } | |
| 945 | |
| 946 static inline void | |
| 947 hb_ot_substitute_pre (const hb_ot_shape_context_t *c) | |
| 948 { | |
| 949 hb_ot_substitute_default (c); | |
| 950 | |
| 951 _hb_buffer_allocate_gsubgpos_vars (c->buffer); | |
| 952 | |
| 953 hb_ot_substitute_plan (c); | |
| 954 | |
| 955 #ifndef HB_NO_AAT_SHAPE | |
| 956 if (c->plan->apply_morx && c->plan->apply_gpos) | |
| 957 hb_aat_layout_remove_deleted_glyphs (c->buffer); | |
| 958 #endif | |
| 959 } | |
| 960 | |
| 961 static inline void | |
| 962 hb_ot_substitute_post (const hb_ot_shape_context_t *c) | |
| 963 { | |
| 964 #ifndef HB_NO_AAT_SHAPE | |
| 965 if (c->plan->apply_morx && !c->plan->apply_gpos) | |
| 966 hb_aat_layout_remove_deleted_glyphs (c->buffer); | |
| 967 #endif | |
| 968 | |
| 969 hb_ot_hide_default_ignorables (c->buffer, c->font); | |
| 970 | |
| 971 if (c->plan->shaper->postprocess_glyphs && | |
| 972 c->buffer->message(c->font, "start postprocess-glyphs")) { | |
| 973 c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); | |
| 974 (void) c->buffer->message(c->font, "end postprocess-glyphs"); | |
| 975 } | |
| 976 } | |
| 977 | |
| 978 | |
| 979 /* | |
| 980 * Position | |
| 981 */ | |
| 982 | |
| 983 static inline void | |
| 984 adjust_mark_offsets (hb_glyph_position_t *pos) | |
| 985 { | |
| 986 pos->x_offset -= pos->x_advance; | |
| 987 pos->y_offset -= pos->y_advance; | |
| 988 } | |
| 989 | |
| 990 static inline void | |
| 991 zero_mark_width (hb_glyph_position_t *pos) | |
| 992 { | |
| 993 pos->x_advance = 0; | |
| 994 pos->y_advance = 0; | |
| 995 } | |
| 996 | |
| 997 static inline void | |
| 998 zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets) | |
| 999 { | |
| 1000 unsigned int count = buffer->len; | |
| 1001 hb_glyph_info_t *info = buffer->info; | |
| 1002 for (unsigned int i = 0; i < count; i++) | |
| 1003 if (_hb_glyph_info_is_mark (&info[i])) | |
| 1004 { | |
| 1005 if (adjust_offsets) | |
| 1006 adjust_mark_offsets (&buffer->pos[i]); | |
| 1007 zero_mark_width (&buffer->pos[i]); | |
| 1008 } | |
| 1009 } | |
| 1010 | |
| 1011 static inline void | |
| 1012 hb_ot_position_default (const hb_ot_shape_context_t *c) | |
| 1013 { | |
| 1014 hb_direction_t direction = c->buffer->props.direction; | |
| 1015 unsigned int count = c->buffer->len; | |
| 1016 hb_glyph_info_t *info = c->buffer->info; | |
| 1017 hb_glyph_position_t *pos = c->buffer->pos; | |
| 1018 | |
| 1019 if (HB_DIRECTION_IS_HORIZONTAL (direction)) | |
| 1020 { | |
| 1021 c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]), | |
| 1022 &pos[0].x_advance, sizeof(pos[0])); | |
| 1023 /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ | |
| 1024 if (c->font->has_glyph_h_origin_func ()) | |
| 1025 for (unsigned int i = 0; i < count; i++) | |
| 1026 c->font->subtract_glyph_h_origin (info[i].codepoint, | |
| 1027 &pos[i].x_offset, | |
| 1028 &pos[i].y_offset); | |
| 1029 } | |
| 1030 else | |
| 1031 { | |
| 1032 c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]), | |
| 1033 &pos[0].y_advance, sizeof(pos[0])); | |
| 1034 for (unsigned int i = 0; i < count; i++) | |
| 1035 { | |
| 1036 c->font->subtract_glyph_v_origin (info[i].codepoint, | |
| 1037 &pos[i].x_offset, | |
| 1038 &pos[i].y_offset); | |
| 1039 } | |
| 1040 } | |
| 1041 if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK) | |
| 1042 _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer); | |
| 1043 } | |
| 1044 | |
| 1045 static inline void | |
| 1046 hb_ot_position_plan (const hb_ot_shape_context_t *c) | |
| 1047 { | |
| 1048 unsigned int count = c->buffer->len; | |
| 1049 hb_glyph_info_t *info = c->buffer->info; | |
| 1050 hb_glyph_position_t *pos = c->buffer->pos; | |
| 1051 | |
| 1052 /* If the font has no GPOS and direction is forward, then when | |
| 1053 * zeroing mark widths, we shift the mark with it, such that the | |
| 1054 * mark is positioned hanging over the previous glyph. When | |
| 1055 * direction is backward we don't shift and it will end up | |
| 1056 * hanging over the next glyph after the final reordering. | |
| 1057 * | |
| 1058 * Note: If fallback positinoing happens, we don't care about | |
| 1059 * this as it will be overridden. | |
| 1060 */ | |
| 1061 bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing && | |
| 1062 HB_DIRECTION_IS_FORWARD (c->buffer->props.direction); | |
| 1063 | |
| 1064 /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ | |
| 1065 | |
| 1066 /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ | |
| 1067 if (c->font->has_glyph_h_origin_func ()) | |
| 1068 for (unsigned int i = 0; i < count; i++) | |
| 1069 c->font->add_glyph_h_origin (info[i].codepoint, | |
| 1070 &pos[i].x_offset, | |
| 1071 &pos[i].y_offset); | |
| 1072 | |
| 1073 hb_ot_layout_position_start (c->font, c->buffer); | |
| 1074 | |
| 1075 if (c->plan->zero_marks) | |
| 1076 switch (c->plan->shaper->zero_width_marks) | |
| 1077 { | |
| 1078 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: | |
| 1079 zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); | |
| 1080 break; | |
| 1081 | |
| 1082 default: | |
| 1083 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: | |
| 1084 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: | |
| 1085 break; | |
| 1086 } | |
| 1087 | |
| 1088 c->plan->position (c->font, c->buffer); | |
| 1089 | |
| 1090 if (c->plan->zero_marks) | |
| 1091 switch (c->plan->shaper->zero_width_marks) | |
| 1092 { | |
| 1093 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: | |
| 1094 zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); | |
| 1095 break; | |
| 1096 | |
| 1097 default: | |
| 1098 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: | |
| 1099 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: | |
| 1100 break; | |
| 1101 } | |
| 1102 | |
| 1103 /* Finish off. Has to follow a certain order. */ | |
| 1104 hb_ot_layout_position_finish_advances (c->font, c->buffer); | |
| 1105 hb_ot_zero_width_default_ignorables (c->buffer); | |
| 1106 #ifndef HB_NO_AAT_SHAPE | |
| 1107 if (c->plan->apply_morx) | |
| 1108 hb_aat_layout_zero_width_deleted_glyphs (c->buffer); | |
| 1109 #endif | |
| 1110 hb_ot_layout_position_finish_offsets (c->font, c->buffer); | |
| 1111 | |
| 1112 /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ | |
| 1113 if (c->font->has_glyph_h_origin_func ()) | |
| 1114 for (unsigned int i = 0; i < count; i++) | |
| 1115 c->font->subtract_glyph_h_origin (info[i].codepoint, | |
| 1116 &pos[i].x_offset, | |
| 1117 &pos[i].y_offset); | |
| 1118 | |
| 1119 if (c->plan->fallback_mark_positioning) | |
| 1120 _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer, | |
| 1121 adjust_offsets_when_zeroing); | |
| 1122 } | |
| 1123 | |
| 1124 static inline void | |
| 1125 hb_ot_position (const hb_ot_shape_context_t *c) | |
| 1126 { | |
| 1127 c->buffer->clear_positions (); | |
| 1128 | |
| 1129 hb_ot_position_default (c); | |
| 1130 | |
| 1131 hb_ot_position_plan (c); | |
| 1132 | |
| 1133 if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) | |
| 1134 hb_buffer_reverse (c->buffer); | |
| 1135 | |
| 1136 _hb_buffer_deallocate_gsubgpos_vars (c->buffer); | |
| 1137 } | |
| 1138 | |
| 1139 static inline void | |
| 1140 hb_propagate_flags (hb_buffer_t *buffer) | |
| 1141 { | |
| 1142 /* Propagate cluster-level glyph flags to be the same on all cluster glyphs. | |
| 1143 * Simplifies using them. */ | |
| 1144 | |
| 1145 if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS)) | |
| 1146 return; | |
| 1147 | |
| 1148 /* If we are producing SAFE_TO_INSERT_TATWEEL, then do two things: | |
| 1149 * | |
| 1150 * - If the places that the Arabic shaper marked as SAFE_TO_INSERT_TATWEEL, | |
| 1151 * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_TATWEEL, | |
| 1152 * - Any place that is SAFE_TO_INSERT_TATWEEL, is also now UNSAFE_TO_BREAK. | |
| 1153 * | |
| 1154 * We couldn't make this interaction earlier. It has to be done here. | |
| 1155 */ | |
| 1156 bool flip_tatweel = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL; | |
| 1157 | |
| 1158 bool clear_concat = (buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0; | |
| 1159 | |
| 1160 hb_glyph_info_t *info = buffer->info; | |
| 1161 | |
| 1162 foreach_cluster (buffer, start, end) | |
| 1163 { | |
| 1164 unsigned int mask = 0; | |
| 1165 for (unsigned int i = start; i < end; i++) | |
| 1166 mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED; | |
| 1167 | |
| 1168 if (flip_tatweel) | |
| 1169 { | |
| 1170 if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) | |
| 1171 mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; | |
| 1172 if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) | |
| 1173 mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; | |
| 1174 } | |
| 1175 | |
| 1176 if (clear_concat) | |
| 1177 mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; | |
| 1178 | |
| 1179 for (unsigned int i = start; i < end; i++) | |
| 1180 info[i].mask = mask; | |
| 1181 } | |
| 1182 } | |
| 1183 | |
| 1184 /* Pull it all together! */ | |
| 1185 | |
| 1186 static void | |
| 1187 hb_ot_shape_internal (hb_ot_shape_context_t *c) | |
| 1188 { | |
| 1189 /* Save the original direction, we use it later. */ | |
| 1190 c->target_direction = c->buffer->props.direction; | |
| 1191 | |
| 1192 _hb_buffer_allocate_unicode_vars (c->buffer); | |
| 1193 | |
| 1194 hb_ot_shape_initialize_masks (c); | |
| 1195 hb_set_unicode_props (c->buffer); | |
| 1196 hb_insert_dotted_circle (c->buffer, c->font); | |
| 1197 | |
| 1198 hb_form_clusters (c->buffer); | |
| 1199 | |
| 1200 hb_ensure_native_direction (c->buffer); | |
| 1201 | |
| 1202 if (c->plan->shaper->preprocess_text && | |
| 1203 c->buffer->message(c->font, "start preprocess-text")) | |
| 1204 { | |
| 1205 c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); | |
| 1206 (void) c->buffer->message(c->font, "end preprocess-text"); | |
| 1207 } | |
| 1208 | |
| 1209 hb_ot_substitute_pre (c); | |
| 1210 hb_ot_position (c); | |
| 1211 hb_ot_substitute_post (c); | |
| 1212 | |
| 1213 hb_propagate_flags (c->buffer); | |
| 1214 | |
| 1215 _hb_buffer_deallocate_unicode_vars (c->buffer); | |
| 1216 | |
| 1217 c->buffer->props.direction = c->target_direction; | |
| 1218 | |
| 1219 c->buffer->leave (); | |
| 1220 } | |
| 1221 | |
| 1222 | |
| 1223 hb_bool_t | |
| 1224 _hb_ot_shape (hb_shape_plan_t *shape_plan, | |
| 1225 hb_font_t *font, | |
| 1226 hb_buffer_t *buffer, | |
| 1227 const hb_feature_t *features, | |
| 1228 unsigned int num_features) | |
| 1229 { | |
| 1230 hb_ot_shape_context_t c = {&shape_plan->ot, font, font->face, buffer, features, num_features}; | |
| 1231 hb_ot_shape_internal (&c); | |
| 1232 | |
| 1233 return true; | |
| 1234 } | |
| 1235 | |
| 1236 | |
| 1237 /** | |
| 1238 * hb_ot_shape_plan_collect_lookups: | |
| 1239 * @shape_plan: #hb_shape_plan_t to query | |
| 1240 * @table_tag: GSUB or GPOS | |
| 1241 * @lookup_indexes: (out): The #hb_set_t set of lookups returned | |
| 1242 * | |
| 1243 * Computes the complete set of GSUB or GPOS lookups that are applicable | |
| 1244 * under a given @shape_plan. | |
| 1245 * | |
| 1246 * Since: 0.9.7 | |
| 1247 **/ | |
| 1248 void | |
| 1249 hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, | |
| 1250 hb_tag_t table_tag, | |
| 1251 hb_set_t *lookup_indexes /* OUT */) | |
| 1252 { | |
| 1253 shape_plan->ot.collect_lookups (table_tag, lookup_indexes); | |
| 1254 } | |
| 1255 | |
| 1256 | |
| 1257 /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ | |
| 1258 static void | |
| 1259 add_char (hb_font_t *font, | |
| 1260 hb_unicode_funcs_t *unicode, | |
| 1261 hb_bool_t mirror, | |
| 1262 hb_codepoint_t u, | |
| 1263 hb_set_t *glyphs) | |
| 1264 { | |
| 1265 hb_codepoint_t glyph; | |
| 1266 if (font->get_nominal_glyph (u, &glyph)) | |
| 1267 glyphs->add (glyph); | |
| 1268 if (mirror) | |
| 1269 { | |
| 1270 hb_codepoint_t m = unicode->mirroring (u); | |
| 1271 if (m != u && font->get_nominal_glyph (m, &glyph)) | |
| 1272 glyphs->add (glyph); | |
| 1273 } | |
| 1274 } | |
| 1275 | |
| 1276 | |
| 1277 /** | |
| 1278 * hb_ot_shape_glyphs_closure: | |
| 1279 * @font: #hb_font_t to work upon | |
| 1280 * @buffer: The input buffer to compute from | |
| 1281 * @features: (array length=num_features): The features enabled on the buffer | |
| 1282 * @num_features: The number of features enabled on the buffer | |
| 1283 * @glyphs: (out): The #hb_set_t set of glyphs comprising the transitive closure of the query | |
| 1284 * | |
| 1285 * Computes the transitive closure of glyphs needed for a specified | |
| 1286 * input buffer under the given font and feature list. The closure is | |
| 1287 * computed as a set, not as a list. | |
| 1288 * | |
| 1289 * Since: 0.9.2 | |
| 1290 **/ | |
| 1291 void | |
| 1292 hb_ot_shape_glyphs_closure (hb_font_t *font, | |
| 1293 hb_buffer_t *buffer, | |
| 1294 const hb_feature_t *features, | |
| 1295 unsigned int num_features, | |
| 1296 hb_set_t *glyphs) | |
| 1297 { | |
| 1298 const char *shapers[] = {"ot", nullptr}; | |
| 1299 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, | |
| 1300 features, num_features, shapers); | |
| 1301 | |
| 1302 bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL; | |
| 1303 | |
| 1304 unsigned int count = buffer->len; | |
| 1305 hb_glyph_info_t *info = buffer->info; | |
| 1306 for (unsigned int i = 0; i < count; i++) | |
| 1307 add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs); | |
| 1308 | |
| 1309 hb_set_t *lookups = hb_set_create (); | |
| 1310 hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups); | |
| 1311 hb_ot_layout_lookups_substitute_closure (font->face, lookups, glyphs); | |
| 1312 | |
| 1313 hb_set_destroy (lookups); | |
| 1314 | |
| 1315 hb_shape_plan_destroy (shape_plan); | |
| 1316 } | |
| 1317 | |
| 1318 | |
| 1319 #endif |
