Coverage Report

Created: 2025-01-11 06:55

/src/mupdf/thirdparty/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
50
static inline bool
51
_hb_codepoint_is_regional_indicator (hb_codepoint_t u)
52
0
{ 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
6
{
58
  /* https://github.com/harfbuzz/harfbuzz/issues/2124 */
59
6
  return hb_aat_layout_has_substitution (face) &&
60
6
   (HB_DIRECTION_IS_HORIZONTAL (props.direction) || !hb_ot_layout_has_substitution (face));
61
6
}
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
6
            face (face),
82
6
            props (props),
83
6
            map (face, props),
84
6
            aat_map (face, props)
85
#ifndef HB_NO_AAT_SHAPE
86
6
            , apply_morx (_hb_apply_morx (face, props))
87
#endif
88
6
{
89
6
  shaper = hb_ot_shaper_categorize (this);
90
91
6
  script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE;
92
6
  script_fallback_mark_positioning = shaper->fallback_position;
93
94
6
#ifndef HB_NO_AAT_SHAPE
95
  /* https://github.com/harfbuzz/harfbuzz/issues/1528 */
96
6
  if (apply_morx && shaper != &_hb_ot_shaper_default)
97
0
    shaper = &_hb_ot_shaper_dumber;
98
6
#endif
99
6
}
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
6
{
105
6
  plan.props = props;
106
6
  plan.shaper = shaper;
107
6
  map.compile (plan.map, key);
108
6
#ifndef HB_NO_AAT_SHAPE
109
6
  if (apply_morx)
110
0
    aat_map.compile (plan.aat_map);
111
6
#endif
112
113
6
#ifndef HB_NO_OT_SHAPE_FRACTIONS
114
6
  plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c'));
115
6
  plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r'));
116
6
  plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m'));
117
6
  plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask);
118
6
#endif
119
120
6
  plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m'));
121
6
  plan.has_vert = !!plan.map.get_1_mask (HB_TAG ('v','e','r','t'));
122
123
6
  hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ?
124
6
          HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n');
125
6
#ifndef HB_NO_OT_KERN
126
6
  plan.kern_mask = plan.map.get_mask (kern_tag);
127
6
  plan.requested_kerning = !!plan.kern_mask;
128
6
#endif
129
6
#ifndef HB_NO_AAT_SHAPE
130
6
  plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k'));
131
6
  plan.requested_tracking = !!plan.trak_mask;
132
6
#endif
133
134
6
  bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX;
135
6
  bool disable_gpos = plan.shaper->gpos_tag &&
136
6
          plan.shaper->gpos_tag != plan.map.chosen_script[1];
137
138
  /*
139
   * Decide who provides glyph classes. GDEF or Unicode.
140
   */
141
142
6
  if (!hb_ot_layout_has_glyph_classes (face))
143
0
    plan.fallback_glyph_classes = true;
144
145
  /*
146
   * Decide who does substitutions. GSUB, morx, or fallback.
147
   */
148
149
6
#ifndef HB_NO_AAT_SHAPE
150
6
  plan.apply_morx = apply_morx;
151
6
#endif
152
153
  /*
154
   * Decide who does positioning. GPOS, kerx, kern, or fallback.
155
   */
156
157
6
#ifndef HB_NO_AAT_SHAPE
158
6
  bool has_kerx = hb_aat_layout_has_positioning (face);
159
6
  bool has_gsub = !apply_morx && hb_ot_layout_has_substitution (face);
160
6
#endif
161
6
  bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face);
162
6
  if (false)
163
0
    ;
164
6
#ifndef HB_NO_AAT_SHAPE
165
  /* Prefer GPOS over kerx if GSUB is present;
166
   * https://github.com/harfbuzz/harfbuzz/issues/3008 */
167
6
  else if (has_kerx && !(has_gsub && has_gpos))
168
0
    plan.apply_kerx = true;
169
6
#endif
170
6
  else if (has_gpos)
171
6
    plan.apply_gpos = true;
172
173
6
  if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos))
174
3
  {
175
3
#ifndef HB_NO_AAT_SHAPE
176
3
    if (has_kerx)
177
0
      plan.apply_kerx = true;
178
3
    else
179
3
#endif
180
3
#ifndef HB_NO_OT_KERN
181
3
    if (hb_ot_layout_has_kerning (face))
182
0
      plan.apply_kern = true;
183
3
#endif
184
3
  }
185
186
6
  plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern);
187
188
6
  plan.zero_marks = script_zero_marks &&
189
6
        !plan.apply_kerx &&
190
6
        (!plan.apply_kern
191
6
#ifndef HB_NO_OT_KERN
192
6
         || !hb_ot_layout_has_machine_kerning (face)
193
6
#endif
194
6
        );
195
6
  plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k'));
196
197
6
  plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos &&
198
6
                !plan.apply_kerx &&
199
6
                (!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
6
  plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing &&
206
6
           script_fallback_mark_positioning;
207
208
6
#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
6
  if (plan.apply_morx)
213
0
    plan.adjust_mark_positioning_when_zeroing = false;
214
215
  /* Currently we always apply trak. */
216
6
  plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face);
217
6
#endif
218
6
}
219
220
bool
221
hb_ot_shape_plan_t::init0 (hb_face_t                     *face,
222
         const hb_shape_plan_key_t     *key)
223
6
{
224
6
  map.init ();
225
6
#ifndef HB_NO_AAT_SHAPE
226
6
  aat_map.init ();
227
6
#endif
228
229
6
  hb_ot_shape_planner_t planner (face,
230
6
         key->props);
231
232
6
  hb_ot_shape_collect_features (&planner,
233
6
        key->user_features,
234
6
        key->num_user_features);
235
236
6
  planner.compile (*this, key->ot);
237
238
6
  if (shaper->data_create)
239
0
  {
240
0
    data = shaper->data_create (this);
241
0
    if (unlikely (!data))
242
0
    {
243
0
      map.fini ();
244
0
#ifndef HB_NO_AAT_SHAPE
245
0
      aat_map.fini ();
246
0
#endif
247
0
      return false;
248
0
    }
249
0
  }
250
251
6
  return true;
252
6
}
253
254
void
255
hb_ot_shape_plan_t::fini ()
256
6
{
257
6
  if (shaper->data_destroy)
258
0
    shaper->data_destroy (const_cast<void *> (data));
259
260
6
  map.fini ();
261
6
#ifndef HB_NO_AAT_SHAPE
262
6
  aat_map.fini ();
263
6
#endif
264
6
}
265
266
void
267
hb_ot_shape_plan_t::substitute (hb_font_t   *font,
268
        hb_buffer_t *buffer) const
269
13
{
270
13
#ifndef HB_NO_AAT_SHAPE
271
13
  if (unlikely (apply_morx))
272
0
    hb_aat_layout_substitute (this, font, buffer);
273
13
  else
274
13
#endif
275
13
    map.substitute (this, font, buffer);
276
13
}
277
278
void
279
hb_ot_shape_plan_t::position (hb_font_t   *font,
280
            hb_buffer_t *buffer) const
281
13
{
282
13
  if (this->apply_gpos)
283
13
    map.position (this, font, buffer);
284
0
#ifndef HB_NO_AAT_SHAPE
285
0
  else if (this->apply_kerx)
286
0
    hb_aat_layout_position (this, font, buffer);
287
13
#endif
288
289
13
#ifndef HB_NO_OT_KERN
290
13
  if (this->apply_kern)
291
0
    hb_ot_layout_kern (this, font, buffer);
292
13
#endif
293
13
  else if (this->apply_fallback_kern)
294
0
    _hb_ot_shape_fallback_kern (this, font, buffer);
295
296
13
#ifndef HB_NO_AAT_SHAPE
297
13
  if (this->apply_trak)
298
0
    hb_aat_layout_track (this, font, buffer);
299
13
#endif
300
13
}
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
6
{
333
6
  hb_ot_map_builder_t *map = &planner->map;
334
335
6
  map->enable_feature (HB_TAG('r','v','r','n'));
336
6
  map->add_gsub_pause (nullptr);
337
338
6
  switch (planner->props.direction)
339
6
  {
340
6
    case HB_DIRECTION_LTR:
341
6
      map->enable_feature (HB_TAG ('l','t','r','a'));
342
6
      map->enable_feature (HB_TAG ('l','t','r','m'));
343
6
      break;
344
0
    case HB_DIRECTION_RTL:
345
0
      map->enable_feature (HB_TAG ('r','t','l','a'));
346
0
      map->add_feature (HB_TAG ('r','t','l','m'));
347
0
      break;
348
0
    case HB_DIRECTION_TTB:
349
0
    case HB_DIRECTION_BTT:
350
0
    case HB_DIRECTION_INVALID:
351
0
    default:
352
0
      break;
353
6
  }
354
355
6
#ifndef HB_NO_OT_SHAPE_FRACTIONS
356
  /* Automatic fractions. */
357
6
  map->add_feature (HB_TAG ('f','r','a','c'));
358
6
  map->add_feature (HB_TAG ('n','u','m','r'));
359
6
  map->add_feature (HB_TAG ('d','n','o','m'));
360
6
#endif
361
362
  /* Random! */
363
6
  map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE);
364
365
6
#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
6
  map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK);
370
6
#endif
371
372
6
  map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */
373
6
  map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */
374
375
6
  if (planner->shaper->collect_features)
376
0
    planner->shaper->collect_features (planner);
377
378
6
  map->enable_feature (HB_TAG ('B','u','z','z')); /* Considered required. */
379
6
  map->enable_feature (HB_TAG ('B','U','Z','Z')); /* Considered discretionary. */
380
381
48
  for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++)
382
42
    map->add_feature (common_features[i]);
383
384
6
  if (HB_DIRECTION_IS_HORIZONTAL (planner->props.direction))
385
48
    for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++)
386
42
      map->add_feature (horizontal_features[i]);
387
0
  else
388
0
  {
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
0
    map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH);
398
0
  }
399
400
6
  for (unsigned int i = 0; i < num_user_features; i++)
401
0
  {
402
0
    const hb_feature_t *feature = &user_features[i];
403
0
    map->add_feature (feature->tag,
404
0
          (feature->start == HB_FEATURE_GLOBAL_START &&
405
0
           feature->end == HB_FEATURE_GLOBAL_END) ?  F_GLOBAL : F_NONE,
406
0
          feature->value);
407
0
  }
408
409
6
#ifndef HB_NO_AAT_SHAPE
410
6
  if (planner->apply_morx)
411
0
  {
412
0
    hb_aat_map_builder_t *aat_map = &planner->aat_map;
413
0
    for (unsigned int i = 0; i < num_user_features; i++)
414
0
    {
415
0
      const hb_feature_t *feature = &user_features[i];
416
0
      aat_map->add_feature (feature->tag, feature->value);
417
0
    }
418
0
  }
419
6
#endif
420
421
6
  if (planner->shaper->override_features)
422
0
    planner->shaper->override_features (planner);
423
6
}
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
6
{
435
6
  return (hb_ot_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
436
6
}
437
438
void
439
_hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data)
440
6
{
441
6
}
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
6
{
453
6
  return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
454
6
}
455
456
void
457
_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED)
458
6
{
459
6
}
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
13
{
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
13
  unsigned int count = buffer->len;
498
13
  hb_glyph_info_t *info = buffer->info;
499
26
  for (unsigned int i = 0; i < count; i++)
500
13
  {
501
13
    _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
13
    if (unlikely (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL &&
506
13
      hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x1F3FBu, 0x1F3FFu)))
507
0
    {
508
0
      _hb_glyph_info_set_continuation (&info[i]);
509
0
    }
510
    /* Regional_Indicators are hairy as hell...
511
     * https://github.com/harfbuzz/harfbuzz/issues/2265 */
512
13
    else if (unlikely (i && _hb_codepoint_is_regional_indicator (info[i].codepoint)))
513
0
    {
514
0
      if (_hb_codepoint_is_regional_indicator (info[i - 1].codepoint) &&
515
0
    !_hb_glyph_info_is_continuation (&info[i - 1]))
516
0
  _hb_glyph_info_set_continuation (&info[i]);
517
0
    }
518
13
#ifndef HB_NO_EMOJI_SEQUENCES
519
13
    else if (unlikely (_hb_glyph_info_is_zwj (&info[i])))
520
0
    {
521
0
      _hb_glyph_info_set_continuation (&info[i]);
522
0
      if (i + 1 < count &&
523
0
    _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint))
524
0
      {
525
0
  i++;
526
0
  _hb_glyph_info_set_unicode_props (&info[i], buffer);
527
0
  _hb_glyph_info_set_continuation (&info[i]);
528
0
      }
529
0
    }
530
13
#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
13
    else if (unlikely (hb_in_ranges<hb_codepoint_t> (info[i].codepoint, 0xFF9Eu, 0xFF9Fu, 0xE0020u, 0xE007Fu)))
546
0
      _hb_glyph_info_set_continuation (&info[i]);
547
13
  }
548
13
}
549
550
static void
551
hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font)
552
13
{
553
13
  if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
554
0
    return;
555
556
13
  if (!(buffer->flags & HB_BUFFER_FLAG_BOT) ||
557
13
      buffer->context_len[0] ||
558
13
      !_hb_glyph_info_is_unicode_mark (&buffer->info[0]))
559
13
    return;
560
561
0
  if (!font->has_glyph (0x25CCu))
562
0
    return;
563
564
0
  hb_glyph_info_t dottedcircle = {0};
565
0
  dottedcircle.codepoint = 0x25CCu;
566
0
  _hb_glyph_info_set_unicode_props (&dottedcircle, buffer);
567
568
0
  buffer->clear_output ();
569
570
0
  buffer->idx = 0;
571
0
  hb_glyph_info_t info = dottedcircle;
572
0
  info.cluster = buffer->cur().cluster;
573
0
  info.mask = buffer->cur().mask;
574
0
  (void) buffer->output_info (info);
575
576
0
  buffer->sync ();
577
0
}
578
579
static void
580
hb_form_clusters (hb_buffer_t *buffer)
581
13
{
582
13
  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
583
7
    return;
584
585
6
  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
586
0
    foreach_grapheme (buffer, start, end)
587
0
      buffer->merge_clusters (start, end);
588
6
  else
589
6
    foreach_grapheme (buffer, start, end)
590
6
      buffer->unsafe_to_break (start, end);
591
6
}
592
593
static void
594
hb_ensure_native_direction (hb_buffer_t *buffer)
595
13
{
596
13
  hb_direction_t direction = buffer->props.direction;
597
13
  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
13
  if (unlikely (horiz_dir == HB_DIRECTION_RTL && direction == HB_DIRECTION_LTR))
617
0
  {
618
0
    bool found_number = false, found_letter = false, found_ri = false;
619
0
    const auto* info = buffer->info;
620
0
    const auto count = buffer->len;
621
0
    for (unsigned i = 0; i < count; i++)
622
0
    {
623
0
      auto gc = _hb_glyph_info_get_general_category (&info[i]);
624
0
      if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
625
0
  found_number = true;
626
0
      else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc))
627
0
      {
628
0
  found_letter = true;
629
0
  break;
630
0
      }
631
0
      else if (_hb_codepoint_is_regional_indicator (info[i].codepoint))
632
0
  found_ri = true;
633
0
    }
634
0
    if ((found_number || found_ri) && !found_letter)
635
0
      horiz_dir = HB_DIRECTION_LTR;
636
0
  }
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
13
  if ((HB_DIRECTION_IS_HORIZONTAL (direction) &&
643
13
       direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) ||
644
13
      (HB_DIRECTION_IS_VERTICAL   (direction) &&
645
13
       direction != HB_DIRECTION_TTB))
646
0
  {
647
0
    _hb_ot_layout_reverse_graphemes (buffer);
648
0
    buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
649
0
  }
650
13
}
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
0
{
661
0
  switch (u >> 8)
662
0
  {
663
0
    case 0x20: switch (u) {
664
0
      case 0x2013u: return 0xfe32u; // EN DASH
665
0
      case 0x2014u: return 0xfe31u; // EM DASH
666
0
      case 0x2025u: return 0xfe30u; // TWO DOT LEADER
667
0
      case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS
668
0
    } break;
669
0
    case 0x30: switch (u) {
670
0
      case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA
671
0
      case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP
672
0
      case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET
673
0
      case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET
674
0
      case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET
675
0
      case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET
676
0
      case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET
677
0
      case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET
678
0
      case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET
679
0
      case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET
680
0
      case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET
681
0
      case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET
682
0
      case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET
683
0
      case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET
684
0
      case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET
685
0
      case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET
686
0
    } break;
687
0
    case 0xfe: switch (u) {
688
0
      case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE
689
0
    } break;
690
0
    case 0xff: switch (u) {
691
0
      case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK
692
0
      case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS
693
0
      case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS
694
0
      case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA
695
0
      case 0xff1au: return 0xfe13u; // FULLWIDTH COLON
696
0
      case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON
697
0
      case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK
698
0
      case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET
699
0
      case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET
700
0
      case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE
701
0
      case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET
702
0
      case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET
703
0
    } break;
704
0
  }
705
706
0
  return u;
707
0
}
708
#endif
709
710
static inline void
711
hb_ot_rotate_chars (const hb_ot_shape_context_t *c)
712
13
{
713
13
  hb_buffer_t *buffer = c->buffer;
714
13
  unsigned int count = buffer->len;
715
13
  hb_glyph_info_t *info = buffer->info;
716
717
13
  if (HB_DIRECTION_IS_BACKWARD (c->target_direction))
718
0
  {
719
0
    hb_unicode_funcs_t *unicode = buffer->unicode;
720
0
    hb_mask_t rtlm_mask = c->plan->rtlm_mask;
721
722
0
    for (unsigned int i = 0; i < count; i++) {
723
0
      hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint);
724
0
      if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint)))
725
0
  info[i].codepoint = codepoint;
726
0
      else
727
0
  info[i].mask |= rtlm_mask;
728
0
    }
729
0
  }
730
731
13
#ifndef HB_NO_VERTICAL
732
13
  if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert)
733
0
  {
734
0
    for (unsigned int i = 0; i < count; i++) {
735
0
      hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint);
736
0
      if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint)))
737
0
  info[i].codepoint = codepoint;
738
0
    }
739
0
  }
740
13
#endif
741
13
}
742
743
static inline void
744
hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c)
745
13
{
746
#ifdef HB_NO_OT_SHAPE_FRACTIONS
747
  return;
748
#endif
749
750
13
  if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
751
13
      !c->plan->has_frac)
752
7
    return;
753
754
6
  hb_buffer_t *buffer = c->buffer;
755
756
6
  hb_mask_t pre_mask, post_mask;
757
6
  if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
758
6
  {
759
6
    pre_mask = c->plan->numr_mask | c->plan->frac_mask;
760
6
    post_mask = c->plan->frac_mask | c->plan->dnom_mask;
761
6
  }
762
0
  else
763
0
  {
764
0
    pre_mask = c->plan->frac_mask | c->plan->dnom_mask;
765
0
    post_mask = c->plan->numr_mask | c->plan->frac_mask;
766
0
  }
767
768
6
  unsigned int count = buffer->len;
769
6
  hb_glyph_info_t *info = buffer->info;
770
12
  for (unsigned int i = 0; i < count; i++)
771
6
  {
772
6
    if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */
773
0
    {
774
0
      unsigned int start = i, end = i + 1;
775
0
      while (start &&
776
0
       _hb_glyph_info_get_general_category (&info[start - 1]) ==
777
0
       HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
778
0
  start--;
779
0
      while (end < count &&
780
0
       _hb_glyph_info_get_general_category (&info[end]) ==
781
0
       HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
782
0
  end++;
783
784
0
      buffer->unsafe_to_break (start, end);
785
786
0
      for (unsigned int j = start; j < i; j++)
787
0
  info[j].mask |= pre_mask;
788
0
      info[i].mask |= c->plan->frac_mask;
789
0
      for (unsigned int j = i + 1; j < end; j++)
790
0
  info[j].mask |= post_mask;
791
792
0
      i = end - 1;
793
0
    }
794
6
  }
795
6
}
796
797
static inline void
798
hb_ot_shape_initialize_masks (const hb_ot_shape_context_t *c)
799
13
{
800
13
  hb_ot_map_t *map = &c->plan->map;
801
13
  hb_buffer_t *buffer = c->buffer;
802
803
13
  hb_mask_t global_mask = map->get_global_mask ();
804
13
  buffer->reset_masks (global_mask);
805
13
}
806
807
static inline void
808
hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c)
809
13
{
810
13
  hb_ot_map_t *map = &c->plan->map;
811
13
  hb_buffer_t *buffer = c->buffer;
812
813
13
  hb_ot_shape_setup_masks_fraction (c);
814
815
13
  if (c->plan->shaper->setup_masks)
816
0
    c->plan->shaper->setup_masks (c->plan, buffer, c->font);
817
818
13
  for (unsigned int i = 0; i < c->num_user_features; i++)
819
0
  {
820
0
    const hb_feature_t *feature = &c->user_features[i];
821
0
    if (!(feature->start == HB_FEATURE_GLOBAL_START && feature->end == HB_FEATURE_GLOBAL_END)) {
822
0
      unsigned int shift;
823
0
      hb_mask_t mask = map->get_mask (feature->tag, &shift);
824
0
      buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
825
0
    }
826
0
  }
827
13
}
828
829
static void
830
hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer)
831
13
{
832
13
  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
833
13
      (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) ||
834
13
      (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES))
835
13
    return;
836
837
0
  unsigned int count = buffer->len;
838
0
  hb_glyph_info_t *info = buffer->info;
839
0
  hb_glyph_position_t *pos = buffer->pos;
840
0
  unsigned int i = 0;
841
0
  for (i = 0; i < count; i++)
842
0
    if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i])))
843
0
      pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0;
844
0
}
845
846
static void
847
hb_ot_hide_default_ignorables (hb_buffer_t *buffer,
848
             hb_font_t   *font)
849
13
{
850
13
  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
851
13
      (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES))
852
13
    return;
853
854
0
  unsigned int count = buffer->len;
855
0
  hb_glyph_info_t *info = buffer->info;
856
857
0
  hb_codepoint_t invisible = buffer->invisible;
858
0
  if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) &&
859
0
      (invisible || font->get_nominal_glyph (' ', &invisible)))
860
0
  {
861
    /* Replace default-ignorables with a zero-advance invisible glyph. */
862
0
    for (unsigned int i = 0; i < count; i++)
863
0
    {
864
0
      if (_hb_glyph_info_is_default_ignorable (&info[i]))
865
0
  info[i].codepoint = invisible;
866
0
    }
867
0
  }
868
0
  else
869
0
    buffer->delete_glyphs_inplace (_hb_glyph_info_is_default_ignorable);
870
0
}
871
872
873
static inline void
874
hb_ot_map_glyphs_fast (hb_buffer_t  *buffer)
875
13
{
876
  /* Normalization process sets up glyph_index(), we just copy it. */
877
13
  unsigned int count = buffer->len;
878
13
  hb_glyph_info_t *info = buffer->info;
879
26
  for (unsigned int i = 0; i < count; i++)
880
13
    info[i].codepoint = info[i].glyph_index();
881
882
13
  buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
883
13
}
884
885
static inline void
886
hb_synthesize_glyph_classes (hb_buffer_t *buffer)
887
0
{
888
0
  unsigned int count = buffer->len;
889
0
  hb_glyph_info_t *info = buffer->info;
890
0
  for (unsigned int i = 0; i < count; i++)
891
0
  {
892
0
    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
0
    klass = (_hb_glyph_info_get_general_category (&info[i]) !=
903
0
       HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ||
904
0
       _hb_glyph_info_is_default_ignorable (&info[i])) ?
905
0
      HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH :
906
0
      HB_OT_LAYOUT_GLYPH_PROPS_MARK;
907
0
    _hb_glyph_info_set_glyph_props (&info[i], klass);
908
0
  }
909
0
}
910
911
static inline void
912
hb_ot_substitute_default (const hb_ot_shape_context_t *c)
913
13
{
914
13
  hb_buffer_t *buffer = c->buffer;
915
916
13
  hb_ot_rotate_chars (c);
917
918
13
  HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index);
919
920
13
  _hb_ot_shape_normalize (c->plan, buffer, c->font);
921
922
13
  hb_ot_shape_setup_masks (c);
923
924
  /* This is unfortunate to go here, but necessary... */
925
13
  if (c->plan->fallback_mark_positioning)
926
0
    _hb_ot_shape_fallback_mark_position_recategorize_marks (c->plan, c->font, buffer);
927
928
13
  hb_ot_map_glyphs_fast (buffer);
929
930
13
  HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index);
931
13
}
932
933
static inline void
934
hb_ot_substitute_plan (const hb_ot_shape_context_t *c)
935
13
{
936
13
  hb_buffer_t *buffer = c->buffer;
937
938
13
  hb_ot_layout_substitute_start (c->font, buffer);
939
940
13
  if (c->plan->fallback_glyph_classes)
941
0
    hb_synthesize_glyph_classes (c->buffer);
942
943
13
  c->plan->substitute (c->font, buffer);
944
13
}
945
946
static inline void
947
hb_ot_substitute_pre (const hb_ot_shape_context_t *c)
948
13
{
949
13
  hb_ot_substitute_default (c);
950
951
13
  _hb_buffer_allocate_gsubgpos_vars (c->buffer);
952
953
13
  hb_ot_substitute_plan (c);
954
955
13
#ifndef HB_NO_AAT_SHAPE
956
13
  if (c->plan->apply_morx && c->plan->apply_gpos)
957
0
    hb_aat_layout_remove_deleted_glyphs (c->buffer);
958
13
#endif
959
13
}
960
961
static inline void
962
hb_ot_substitute_post (const hb_ot_shape_context_t *c)
963
13
{
964
13
#ifndef HB_NO_AAT_SHAPE
965
13
  if (c->plan->apply_morx && !c->plan->apply_gpos)
966
0
    hb_aat_layout_remove_deleted_glyphs (c->buffer);
967
13
#endif
968
969
13
  hb_ot_hide_default_ignorables (c->buffer, c->font);
970
971
13
  if (c->plan->shaper->postprocess_glyphs &&
972
13
    c->buffer->message(c->font, "start postprocess-glyphs")) {
973
0
    c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
974
0
    (void) c->buffer->message(c->font, "end postprocess-glyphs");
975
0
  }
976
13
}
977
978
979
/*
980
 * Position
981
 */
982
983
static inline void
984
adjust_mark_offsets (hb_glyph_position_t *pos)
985
0
{
986
0
  pos->x_offset -= pos->x_advance;
987
0
  pos->y_offset -= pos->y_advance;
988
0
}
989
990
static inline void
991
zero_mark_width (hb_glyph_position_t *pos)
992
0
{
993
0
  pos->x_advance = 0;
994
0
  pos->y_advance = 0;
995
0
}
996
997
static inline void
998
zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets)
999
13
{
1000
13
  unsigned int count = buffer->len;
1001
13
  hb_glyph_info_t *info = buffer->info;
1002
26
  for (unsigned int i = 0; i < count; i++)
1003
13
    if (_hb_glyph_info_is_mark (&info[i]))
1004
0
    {
1005
0
      if (adjust_offsets)
1006
0
  adjust_mark_offsets (&buffer->pos[i]);
1007
0
      zero_mark_width (&buffer->pos[i]);
1008
0
    }
1009
13
}
1010
1011
static inline void
1012
hb_ot_position_default (const hb_ot_shape_context_t *c)
1013
13
{
1014
13
  hb_direction_t direction = c->buffer->props.direction;
1015
13
  unsigned int count = c->buffer->len;
1016
13
  hb_glyph_info_t *info = c->buffer->info;
1017
13
  hb_glyph_position_t *pos = c->buffer->pos;
1018
1019
13
  if (HB_DIRECTION_IS_HORIZONTAL (direction))
1020
13
  {
1021
13
    c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]),
1022
13
           &pos[0].x_advance, sizeof(pos[0]));
1023
    /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
1024
13
    if (c->font->has_glyph_h_origin_func ())
1025
0
      for (unsigned int i = 0; i < count; i++)
1026
0
  c->font->subtract_glyph_h_origin (info[i].codepoint,
1027
0
            &pos[i].x_offset,
1028
0
            &pos[i].y_offset);
1029
13
  }
1030
0
  else
1031
0
  {
1032
0
    c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]),
1033
0
           &pos[0].y_advance, sizeof(pos[0]));
1034
0
    for (unsigned int i = 0; i < count; i++)
1035
0
    {
1036
0
      c->font->subtract_glyph_v_origin (info[i].codepoint,
1037
0
          &pos[i].x_offset,
1038
0
          &pos[i].y_offset);
1039
0
    }
1040
0
  }
1041
13
  if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK)
1042
0
    _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer);
1043
13
}
1044
1045
static inline void
1046
hb_ot_position_plan (const hb_ot_shape_context_t *c)
1047
13
{
1048
13
  unsigned int count = c->buffer->len;
1049
13
  hb_glyph_info_t *info = c->buffer->info;
1050
13
  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
13
  bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing &&
1062
13
             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
13
  if (c->font->has_glyph_h_origin_func ())
1068
0
    for (unsigned int i = 0; i < count; i++)
1069
0
      c->font->add_glyph_h_origin (info[i].codepoint,
1070
0
           &pos[i].x_offset,
1071
0
           &pos[i].y_offset);
1072
1073
13
  hb_ot_layout_position_start (c->font, c->buffer);
1074
1075
13
  if (c->plan->zero_marks)
1076
13
    switch (c->plan->shaper->zero_width_marks)
1077
13
    {
1078
0
      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
1079
0
  zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
1080
0
  break;
1081
1082
0
      default:
1083
0
      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
1084
13
      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
1085
13
  break;
1086
13
    }
1087
1088
13
  c->plan->position (c->font, c->buffer);
1089
1090
13
  if (c->plan->zero_marks)
1091
13
    switch (c->plan->shaper->zero_width_marks)
1092
13
    {
1093
13
      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
1094
13
  zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
1095
13
  break;
1096
1097
0
      default:
1098
0
      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
1099
0
      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
1100
0
  break;
1101
13
    }
1102
1103
  /* Finish off.  Has to follow a certain order. */
1104
13
  hb_ot_layout_position_finish_advances (c->font, c->buffer);
1105
13
  hb_ot_zero_width_default_ignorables (c->buffer);
1106
13
#ifndef HB_NO_AAT_SHAPE
1107
13
  if (c->plan->apply_morx)
1108
0
    hb_aat_layout_zero_width_deleted_glyphs (c->buffer);
1109
13
#endif
1110
13
  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
13
  if (c->font->has_glyph_h_origin_func ())
1114
0
    for (unsigned int i = 0; i < count; i++)
1115
0
      c->font->subtract_glyph_h_origin (info[i].codepoint,
1116
0
          &pos[i].x_offset,
1117
0
          &pos[i].y_offset);
1118
1119
13
  if (c->plan->fallback_mark_positioning)
1120
0
    _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer,
1121
0
           adjust_offsets_when_zeroing);
1122
13
}
1123
1124
static inline void
1125
hb_ot_position (const hb_ot_shape_context_t *c)
1126
13
{
1127
13
  c->buffer->clear_positions ();
1128
1129
13
  hb_ot_position_default (c);
1130
1131
13
  hb_ot_position_plan (c);
1132
1133
13
  if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
1134
0
    hb_buffer_reverse (c->buffer);
1135
1136
13
  _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
1137
13
}
1138
1139
static inline void
1140
hb_propagate_flags (hb_buffer_t *buffer)
1141
13
{
1142
  /* Propagate cluster-level glyph flags to be the same on all cluster glyphs.
1143
   * Simplifies using them. */
1144
1145
13
  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS))
1146
13
    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
0
  bool flip_tatweel = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL;
1157
1158
0
  bool clear_concat = (buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0;
1159
1160
0
  hb_glyph_info_t *info = buffer->info;
1161
1162
0
  foreach_cluster (buffer, start, end)
1163
0
  {
1164
0
    unsigned int mask = 0;
1165
0
    for (unsigned int i = start; i < end; i++)
1166
0
      mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED;
1167
1168
0
    if (flip_tatweel)
1169
0
    {
1170
0
      if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
1171
0
  mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL;
1172
0
      if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL)
1173
0
  mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT;
1174
0
    }
1175
1176
0
    if (clear_concat)
1177
0
  mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT;
1178
1179
0
    for (unsigned int i = start; i < end; i++)
1180
0
      info[i].mask = mask;
1181
0
  }
1182
0
}
1183
1184
/* Pull it all together! */
1185
1186
static void
1187
hb_ot_shape_internal (hb_ot_shape_context_t *c)
1188
13
{
1189
  /* Save the original direction, we use it later. */
1190
13
  c->target_direction = c->buffer->props.direction;
1191
1192
13
  _hb_buffer_allocate_unicode_vars (c->buffer);
1193
1194
13
  hb_ot_shape_initialize_masks (c);
1195
13
  hb_set_unicode_props (c->buffer);
1196
13
  hb_insert_dotted_circle (c->buffer, c->font);
1197
1198
13
  hb_form_clusters (c->buffer);
1199
1200
13
  hb_ensure_native_direction (c->buffer);
1201
1202
13
  if (c->plan->shaper->preprocess_text &&
1203
13
      c->buffer->message(c->font, "start preprocess-text"))
1204
0
  {
1205
0
    c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
1206
0
    (void) c->buffer->message(c->font, "end preprocess-text");
1207
0
  }
1208
1209
13
  hb_ot_substitute_pre (c);
1210
13
  hb_ot_position (c);
1211
13
  hb_ot_substitute_post (c);
1212
1213
13
  hb_propagate_flags (c->buffer);
1214
1215
13
  _hb_buffer_deallocate_unicode_vars (c->buffer);
1216
1217
13
  c->buffer->props.direction = c->target_direction;
1218
1219
13
  c->buffer->leave ();
1220
13
}
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
13
{
1230
13
  hb_ot_shape_context_t c = {&shape_plan->ot, font, font->face, buffer, features, num_features};
1231
13
  hb_ot_shape_internal (&c);
1232
1233
13
  return true;
1234
13
}
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
0
{
1253
0
  shape_plan->ot.collect_lookups (table_tag, lookup_indexes);
1254
0
}
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
0
{
1265
0
  hb_codepoint_t glyph;
1266
0
  if (font->get_nominal_glyph (u, &glyph))
1267
0
    glyphs->add (glyph);
1268
0
  if (mirror)
1269
0
  {
1270
0
    hb_codepoint_t m = unicode->mirroring (u);
1271
0
    if (m != u && font->get_nominal_glyph (m, &glyph))
1272
0
      glyphs->add (glyph);
1273
0
  }
1274
0
}
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
0
{
1298
0
  const char *shapers[] = {"ot", nullptr};
1299
0
  hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
1300
0
                   features, num_features, shapers);
1301
1302
0
  bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL;
1303
1304
0
  unsigned int count = buffer->len;
1305
0
  hb_glyph_info_t *info = buffer->info;
1306
0
  for (unsigned int i = 0; i < count; i++)
1307
0
    add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs);
1308
1309
0
  hb_set_t *lookups = hb_set_create ();
1310
0
  hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups);
1311
0
  hb_ot_layout_lookups_substitute_closure (font->face, lookups, glyphs);
1312
1313
0
  hb_set_destroy (lookups);
1314
1315
0
  hb_shape_plan_destroy (shape_plan);
1316
0
}
1317
1318
1319
#endif