Coverage Report

Created: 2025-07-07 10:01

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