2009-12-20 20:58:26 +01:00
|
|
|
/*
|
2011-04-21 23:14:28 +02:00
|
|
|
* Copyright © 2009,2010 Red Hat, Inc.
|
2012-08-10 09:28:50 +02:00
|
|
|
* Copyright © 2010,2011,2012 Google, Inc.
|
2009-12-20 20:58:26 +01:00
|
|
|
*
|
2010-04-22 06:11:43 +02:00
|
|
|
* This is part of HarfBuzz, a text shaping library.
|
2009-12-20 20:58:26 +01:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, without written agreement and without
|
|
|
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|
|
|
* software and its documentation for any purpose, provided that the
|
|
|
|
* above copyright notice and the following two paragraphs appear in
|
|
|
|
* all copies of this software.
|
|
|
|
*
|
|
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
|
|
|
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
|
|
|
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
|
|
* DAMAGE.
|
|
|
|
*
|
|
|
|
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
|
|
|
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
|
|
|
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
|
|
*
|
|
|
|
* Red Hat Author(s): Behdad Esfahbod
|
2010-10-07 23:47:33 +02:00
|
|
|
* Google Author(s): Behdad Esfahbod
|
2009-12-20 20:58:26 +01:00
|
|
|
*/
|
|
|
|
|
2019-06-26 22:21:03 +02:00
|
|
|
#include "hb.hh"
|
|
|
|
|
|
|
|
#ifndef HB_NO_OT_SHAPE
|
|
|
|
|
2019-06-26 22:38:14 +02:00
|
|
|
#ifdef HB_NO_OT_LAYOUT
|
|
|
|
#error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT."
|
|
|
|
#endif
|
|
|
|
|
2018-08-26 07:36:36 +02:00
|
|
|
#include "hb-shaper-impl.hh"
|
2012-07-26 23:34:25 +02:00
|
|
|
|
2018-08-26 07:36:36 +02:00
|
|
|
#include "hb-ot-shape.hh"
|
|
|
|
#include "hb-ot-shape-complex.hh"
|
|
|
|
#include "hb-ot-shape-fallback.hh"
|
|
|
|
#include "hb-ot-shape-normalize.hh"
|
2009-12-20 20:58:26 +01:00
|
|
|
|
2018-08-26 10:15:47 +02:00
|
|
|
#include "hb-ot-face.hh"
|
2018-10-02 13:11:18 +02:00
|
|
|
|
2018-08-26 07:36:36 +02:00
|
|
|
#include "hb-set.hh"
|
2011-05-03 02:46:32 +02:00
|
|
|
|
2018-08-26 07:36:36 +02:00
|
|
|
#include "hb-aat-layout.hh"
|
2010-07-23 21:11:18 +02:00
|
|
|
|
2018-10-02 13:04:05 +02:00
|
|
|
|
2020-01-24 19:49:03 +01:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
|
|
static inline bool
|
|
|
|
_hb_apply_morx (hb_face_t *face, const hb_segment_properties_t *props)
|
|
|
|
{
|
|
|
|
/* https://github.com/harfbuzz/harfbuzz/issues/2124 */
|
|
|
|
return hb_aat_layout_has_substitution (face) &&
|
|
|
|
(HB_DIRECTION_IS_HORIZONTAL (props->direction) || !hb_ot_layout_has_substitution (face));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-10-27 13:58:32 +02:00
|
|
|
/**
|
|
|
|
* SECTION:hb-ot-shape
|
|
|
|
* @title: hb-ot-shape
|
|
|
|
* @short_description: OpenType shaping support
|
|
|
|
* @include: hb-ot.h
|
|
|
|
*
|
|
|
|
* Support functions for OpenType shaping related queries.
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
2018-11-12 03:32:01 +01:00
|
|
|
static void
|
|
|
|
hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
|
|
|
|
const hb_feature_t *user_features,
|
|
|
|
unsigned int num_user_features);
|
|
|
|
|
2018-11-12 04:51:34 +01:00
|
|
|
hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *face,
|
|
|
|
const hb_segment_properties_t *props) :
|
|
|
|
face (face),
|
|
|
|
props (*props),
|
|
|
|
map (face, props),
|
2019-05-13 23:45:51 +02:00
|
|
|
aat_map (face, props)
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2020-01-24 19:49:03 +01:00
|
|
|
, apply_morx (_hb_apply_morx (face, props))
|
2019-05-13 23:45:51 +02:00
|
|
|
#endif
|
2018-11-22 21:52:29 +01:00
|
|
|
{
|
|
|
|
shaper = hb_ot_shape_complex_categorize (this);
|
|
|
|
|
|
|
|
script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE;
|
|
|
|
script_fallback_mark_positioning = shaper->fallback_position;
|
|
|
|
|
2019-12-10 01:10:34 +01:00
|
|
|
/* https://github.com/harfbuzz/harfbuzz/issues/1528 */
|
|
|
|
if (apply_morx && shaper != &_hb_ot_complex_shaper_default)
|
|
|
|
shaper = &_hb_ot_complex_shaper_dumber;
|
2018-11-22 21:52:29 +01:00
|
|
|
}
|
2018-10-23 22:09:30 +02:00
|
|
|
|
2018-10-02 13:04:05 +02:00
|
|
|
void
|
2018-11-13 00:48:10 +01:00
|
|
|
hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
|
|
|
|
const hb_ot_shape_plan_key_t &key)
|
2018-10-02 13:04:05 +02:00
|
|
|
{
|
|
|
|
plan.props = props;
|
|
|
|
plan.shaper = shaper;
|
2018-11-13 00:48:10 +01:00
|
|
|
map.compile (plan.map, key);
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-10-23 23:31:51 +02:00
|
|
|
if (apply_morx)
|
2018-11-13 00:48:10 +01:00
|
|
|
aat_map.compile (plan.aat_map);
|
2019-06-20 02:08:26 +02:00
|
|
|
#endif
|
2018-10-02 13:04:05 +02:00
|
|
|
|
2019-06-20 01:57:48 +02:00
|
|
|
#ifndef HB_NO_OT_SHAPE_FRACTIONS
|
2018-10-02 13:04:05 +02:00
|
|
|
plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c'));
|
|
|
|
plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r'));
|
|
|
|
plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m'));
|
|
|
|
plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask);
|
2019-06-20 01:57:48 +02:00
|
|
|
#endif
|
|
|
|
|
2018-10-23 12:10:56 +02:00
|
|
|
plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m'));
|
2019-12-05 16:28:42 +01:00
|
|
|
plan.has_vert = !!plan.map.get_1_mask (HB_TAG ('v','e','r','t'));
|
|
|
|
|
2018-11-12 03:32:01 +01:00
|
|
|
hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ?
|
2018-10-10 16:42:10 +02:00
|
|
|
HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n');
|
2019-06-20 02:04:16 +02:00
|
|
|
#ifndef HB_NO_OT_KERN
|
2018-10-10 16:42:10 +02:00
|
|
|
plan.kern_mask = plan.map.get_mask (kern_tag);
|
2019-06-20 02:04:16 +02:00
|
|
|
plan.requested_kerning = !!plan.kern_mask;
|
|
|
|
#endif
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-10-23 12:10:56 +02:00
|
|
|
plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k'));
|
2019-06-20 02:02:32 +02:00
|
|
|
plan.requested_tracking = !!plan.trak_mask;
|
|
|
|
#endif
|
2018-10-10 16:42:10 +02:00
|
|
|
|
2018-10-10 16:57:28 +02:00
|
|
|
bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX;
|
2018-10-10 16:42:10 +02:00
|
|
|
bool disable_gpos = plan.shaper->gpos_tag &&
|
|
|
|
plan.shaper->gpos_tag != plan.map.chosen_script[1];
|
2018-10-02 13:24:40 +02:00
|
|
|
|
2018-10-10 16:49:45 +02:00
|
|
|
/*
|
|
|
|
* Decide who provides glyph classes. GDEF or Unicode.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!hb_ot_layout_has_glyph_classes (face))
|
|
|
|
plan.fallback_glyph_classes = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decide who does substitutions. GSUB, morx, or fallback.
|
|
|
|
*/
|
2018-10-10 16:16:09 +02:00
|
|
|
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-10-23 22:09:30 +02:00
|
|
|
plan.apply_morx = apply_morx;
|
2019-05-13 23:45:51 +02:00
|
|
|
#endif
|
2018-10-04 11:34:21 +02:00
|
|
|
|
2018-10-10 16:49:45 +02:00
|
|
|
/*
|
|
|
|
* Decide who does positioning. GPOS, kerx, kern, or fallback.
|
|
|
|
*/
|
2018-10-10 16:16:09 +02:00
|
|
|
|
2021-06-13 13:28:20 +02:00
|
|
|
bool has_gsub = hb_ot_layout_has_substitution (face);
|
|
|
|
bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face);
|
2021-07-16 23:06:10 +02:00
|
|
|
if (false)
|
2019-05-11 08:31:05 +02:00
|
|
|
;
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2021-06-13 13:28:20 +02:00
|
|
|
else if (hb_aat_layout_has_positioning (face) && !(has_gsub && has_gpos))
|
2018-10-10 23:44:46 +02:00
|
|
|
plan.apply_kerx = true;
|
2019-05-11 08:31:05 +02:00
|
|
|
#endif
|
2021-06-13 13:28:20 +02:00
|
|
|
else if (!apply_morx && has_gpos)
|
2018-10-10 16:49:45 +02:00
|
|
|
plan.apply_gpos = true;
|
|
|
|
|
2019-10-31 20:50:47 +01:00
|
|
|
if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos))
|
2018-10-10 16:49:45 +02:00
|
|
|
{
|
2018-10-11 19:26:58 +02:00
|
|
|
/* Apparently Apple applies kerx if GPOS kern was not applied. */
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2019-06-18 23:25:52 +02:00
|
|
|
if (hb_aat_layout_has_positioning (face))
|
2018-10-11 19:26:58 +02:00
|
|
|
plan.apply_kerx = true;
|
2019-06-18 23:25:52 +02:00
|
|
|
else
|
2019-05-13 23:45:51 +02:00
|
|
|
#endif
|
2019-06-20 01:24:51 +02:00
|
|
|
#ifndef HB_NO_OT_KERN
|
2019-06-18 23:25:52 +02:00
|
|
|
if (hb_ot_layout_has_kerning (face))
|
2018-10-11 19:26:58 +02:00
|
|
|
plan.apply_kern = true;
|
2019-06-20 01:24:51 +02:00
|
|
|
#endif
|
2018-10-10 16:49:45 +02:00
|
|
|
}
|
2018-10-10 16:16:09 +02:00
|
|
|
|
2021-07-16 23:06:10 +02:00
|
|
|
plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern);
|
|
|
|
|
2018-11-23 17:10:17 +01:00
|
|
|
plan.zero_marks = script_zero_marks &&
|
|
|
|
!plan.apply_kerx &&
|
2019-06-20 01:24:51 +02:00
|
|
|
(!plan.apply_kern
|
|
|
|
#ifndef HB_NO_OT_KERN
|
|
|
|
|| !hb_ot_layout_has_machine_kerning (face)
|
|
|
|
#endif
|
|
|
|
);
|
2018-10-10 16:16:09 +02:00
|
|
|
plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k'));
|
2018-11-22 21:52:29 +01:00
|
|
|
|
2018-11-22 23:31:07 +01:00
|
|
|
plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos &&
|
|
|
|
!plan.apply_kerx &&
|
2019-06-20 01:24:51 +02:00
|
|
|
(!plan.apply_kern
|
|
|
|
#ifndef HB_NO_OT_KERN
|
|
|
|
|| !hb_ot_layout_has_cross_kerning (face)
|
|
|
|
#endif
|
|
|
|
);
|
2018-11-22 23:31:07 +01:00
|
|
|
|
|
|
|
plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing &&
|
|
|
|
script_fallback_mark_positioning;
|
2018-10-11 19:24:17 +02:00
|
|
|
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2021-05-13 12:39:09 +02:00
|
|
|
/* If we're using morx shaping, we cancel mark position adjustment because
|
|
|
|
Apple Color Emoji assumes this will NOT be done when forming emoji sequences;
|
|
|
|
https://github.com/harfbuzz/harfbuzz/issues/2967. */
|
|
|
|
if (plan.apply_morx)
|
|
|
|
plan.adjust_mark_positioning_when_zeroing = false;
|
|
|
|
|
2018-10-11 19:24:17 +02:00
|
|
|
/* Currently we always apply trak. */
|
2018-10-23 12:10:56 +02:00
|
|
|
plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face);
|
2019-05-13 23:45:51 +02:00
|
|
|
#endif
|
2018-10-02 13:04:05 +02:00
|
|
|
}
|
|
|
|
|
2018-11-12 03:32:01 +01:00
|
|
|
bool
|
2018-11-12 04:51:34 +01:00
|
|
|
hb_ot_shape_plan_t::init0 (hb_face_t *face,
|
2018-11-13 00:05:02 +01:00
|
|
|
const hb_shape_plan_key_t *key)
|
2018-11-12 03:32:01 +01:00
|
|
|
{
|
|
|
|
map.init ();
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-11-12 03:32:01 +01:00
|
|
|
aat_map.init ();
|
2019-06-20 02:08:26 +02:00
|
|
|
#endif
|
2018-11-12 03:32:01 +01:00
|
|
|
|
2018-11-13 00:05:02 +01:00
|
|
|
hb_ot_shape_planner_t planner (face,
|
|
|
|
&key->props);
|
2018-11-22 21:52:29 +01:00
|
|
|
|
2018-11-13 00:05:02 +01:00
|
|
|
hb_ot_shape_collect_features (&planner,
|
|
|
|
key->user_features,
|
|
|
|
key->num_user_features);
|
2018-11-12 03:32:01 +01:00
|
|
|
|
2018-11-13 00:48:10 +01:00
|
|
|
planner.compile (*this, key->ot);
|
2018-11-12 03:32:01 +01:00
|
|
|
|
|
|
|
if (shaper->data_create)
|
|
|
|
{
|
|
|
|
data = shaper->data_create (this);
|
|
|
|
if (unlikely (!data))
|
2020-06-28 05:09:45 +02:00
|
|
|
{
|
|
|
|
map.fini ();
|
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
|
|
aat_map.fini ();
|
|
|
|
#endif
|
2018-11-12 03:32:01 +01:00
|
|
|
return false;
|
2020-06-28 05:09:45 +02:00
|
|
|
}
|
2018-11-12 03:32:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-12-17 19:01:01 +01:00
|
|
|
hb_ot_shape_plan_t::fini ()
|
2018-11-12 03:32:01 +01:00
|
|
|
{
|
|
|
|
if (shaper->data_destroy)
|
|
|
|
shaper->data_destroy (const_cast<void *> (data));
|
|
|
|
|
|
|
|
map.fini ();
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-11-12 03:32:01 +01:00
|
|
|
aat_map.fini ();
|
2019-06-20 02:08:26 +02:00
|
|
|
#endif
|
2018-11-12 03:32:01 +01:00
|
|
|
}
|
|
|
|
|
2018-11-14 20:49:34 +01:00
|
|
|
void
|
|
|
|
hb_ot_shape_plan_t::substitute (hb_font_t *font,
|
|
|
|
hb_buffer_t *buffer) const
|
|
|
|
{
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-11-14 20:49:34 +01:00
|
|
|
if (unlikely (apply_morx))
|
|
|
|
hb_aat_layout_substitute (this, font, buffer);
|
|
|
|
else
|
2019-06-18 23:25:52 +02:00
|
|
|
#endif
|
2018-11-14 20:49:34 +01:00
|
|
|
map.substitute (this, font, buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
hb_ot_shape_plan_t::position (hb_font_t *font,
|
|
|
|
hb_buffer_t *buffer) const
|
|
|
|
{
|
|
|
|
if (this->apply_gpos)
|
|
|
|
map.position (this, font, buffer);
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-11-14 20:49:34 +01:00
|
|
|
else if (this->apply_kerx)
|
|
|
|
hb_aat_layout_position (this, font, buffer);
|
2019-06-18 23:25:52 +02:00
|
|
|
#endif
|
2021-07-16 23:06:10 +02:00
|
|
|
|
2019-06-20 01:24:51 +02:00
|
|
|
#ifndef HB_NO_OT_KERN
|
2021-07-16 23:06:10 +02:00
|
|
|
if (this->apply_kern)
|
2018-11-14 20:49:34 +01:00
|
|
|
hb_ot_layout_kern (this, font, buffer);
|
2019-06-20 01:24:51 +02:00
|
|
|
#endif
|
2021-07-16 23:06:10 +02:00
|
|
|
else if (this->apply_fallback_kern)
|
2018-11-14 20:49:34 +01:00
|
|
|
_hb_ot_shape_fallback_kern (this, font, buffer);
|
|
|
|
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-11-14 20:49:34 +01:00
|
|
|
if (this->apply_trak)
|
|
|
|
hb_aat_layout_track (this, font, buffer);
|
2019-06-18 23:25:52 +02:00
|
|
|
#endif
|
2018-11-14 20:49:34 +01:00
|
|
|
}
|
|
|
|
|
2018-10-02 13:04:05 +02:00
|
|
|
|
2018-10-02 14:45:09 +02:00
|
|
|
static const hb_ot_map_feature_t
|
|
|
|
common_features[] =
|
2018-10-02 13:04:05 +02:00
|
|
|
{
|
2019-08-16 00:48:26 +02:00
|
|
|
{HB_TAG('a','b','v','m'), F_GLOBAL},
|
|
|
|
{HB_TAG('b','l','w','m'), F_GLOBAL},
|
2018-10-02 14:45:09 +02:00
|
|
|
{HB_TAG('c','c','m','p'), F_GLOBAL},
|
|
|
|
{HB_TAG('l','o','c','l'), F_GLOBAL},
|
|
|
|
{HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS},
|
|
|
|
{HB_TAG('m','k','m','k'), F_GLOBAL_MANUAL_JOINERS},
|
|
|
|
{HB_TAG('r','l','i','g'), F_GLOBAL},
|
2011-05-31 21:18:13 +02:00
|
|
|
};
|
|
|
|
|
2012-05-09 15:04:13 +02:00
|
|
|
|
2018-10-02 14:45:09 +02:00
|
|
|
static const hb_ot_map_feature_t
|
|
|
|
horizontal_features[] =
|
2018-10-02 13:04:05 +02:00
|
|
|
{
|
2018-10-02 14:45:09 +02:00
|
|
|
{HB_TAG('c','a','l','t'), F_GLOBAL},
|
|
|
|
{HB_TAG('c','l','i','g'), F_GLOBAL},
|
|
|
|
{HB_TAG('c','u','r','s'), F_GLOBAL},
|
2019-08-16 00:28:41 +02:00
|
|
|
{HB_TAG('d','i','s','t'), F_GLOBAL},
|
2019-08-16 01:00:30 +02:00
|
|
|
{HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK},
|
2018-10-02 14:45:09 +02:00
|
|
|
{HB_TAG('l','i','g','a'), F_GLOBAL},
|
|
|
|
{HB_TAG('r','c','l','t'), F_GLOBAL},
|
2011-05-31 21:18:13 +02:00
|
|
|
};
|
|
|
|
|
2009-12-20 20:58:26 +01:00
|
|
|
static void
|
2011-05-28 00:13:31 +02:00
|
|
|
hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
|
|
|
|
const hb_feature_t *user_features,
|
|
|
|
unsigned int num_user_features)
|
2009-12-20 20:58:26 +01:00
|
|
|
{
|
2012-08-02 16:07:58 +02:00
|
|
|
hb_ot_map_builder_t *map = &planner->map;
|
|
|
|
|
2018-09-25 00:01:53 +02:00
|
|
|
map->enable_feature (HB_TAG('r','v','r','n'));
|
2017-10-15 12:11:08 +02:00
|
|
|
map->add_gsub_pause (nullptr);
|
2016-09-28 17:05:43 +02:00
|
|
|
|
2018-11-13 00:05:02 +01:00
|
|
|
switch (planner->props.direction) {
|
2010-05-29 02:21:47 +02:00
|
|
|
case HB_DIRECTION_LTR:
|
2018-09-25 00:01:53 +02:00
|
|
|
map->enable_feature (HB_TAG ('l','t','r','a'));
|
|
|
|
map->enable_feature (HB_TAG ('l','t','r','m'));
|
2010-05-29 02:21:47 +02:00
|
|
|
break;
|
|
|
|
case HB_DIRECTION_RTL:
|
2018-09-25 00:01:53 +02:00
|
|
|
map->enable_feature (HB_TAG ('r','t','l','a'));
|
|
|
|
map->add_feature (HB_TAG ('r','t','l','m'));
|
2010-05-29 02:21:47 +02:00
|
|
|
break;
|
|
|
|
case HB_DIRECTION_TTB:
|
|
|
|
case HB_DIRECTION_BTT:
|
2011-03-16 18:53:32 +01:00
|
|
|
case HB_DIRECTION_INVALID:
|
2010-05-29 02:21:47 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-06-20 01:57:48 +02:00
|
|
|
#ifndef HB_NO_OT_SHAPE_FRACTIONS
|
2018-09-10 16:24:52 +02:00
|
|
|
/* Automatic fractions. */
|
2018-09-25 00:01:53 +02:00
|
|
|
map->add_feature (HB_TAG ('f','r','a','c'));
|
|
|
|
map->add_feature (HB_TAG ('n','u','m','r'));
|
|
|
|
map->add_feature (HB_TAG ('d','n','o','m'));
|
2019-06-20 01:57:48 +02:00
|
|
|
#endif
|
2013-12-22 22:17:54 +01:00
|
|
|
|
2018-09-10 16:24:52 +02:00
|
|
|
/* Random! */
|
2018-10-02 14:48:39 +02:00
|
|
|
map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE);
|
2018-09-10 16:24:52 +02:00
|
|
|
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-10-23 12:10:56 +02:00
|
|
|
/* Tracking. We enable dummy feature here just to allow disabling
|
|
|
|
* AAT 'trak' table using features.
|
|
|
|
* https://github.com/harfbuzz/harfbuzz/issues/1303 */
|
2018-10-22 23:22:05 +02:00
|
|
|
map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK);
|
2019-06-20 01:51:13 +02:00
|
|
|
#endif
|
2018-10-22 23:22:05 +02:00
|
|
|
|
|
|
|
map->enable_feature (HB_TAG ('H','A','R','F'));
|
2018-10-04 13:00:37 +02:00
|
|
|
|
2012-07-31 03:08:51 +02:00
|
|
|
if (planner->shaper->collect_features)
|
2012-08-02 15:38:28 +02:00
|
|
|
planner->shaper->collect_features (planner);
|
2010-05-29 02:37:06 +02:00
|
|
|
|
2018-10-22 23:22:05 +02:00
|
|
|
map->enable_feature (HB_TAG ('B','U','Z','Z'));
|
2018-10-04 13:00:37 +02:00
|
|
|
|
2013-02-15 12:22:26 +01:00
|
|
|
for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++)
|
2018-10-02 14:45:09 +02:00
|
|
|
map->add_feature (common_features[i]);
|
2011-05-31 21:18:13 +02:00
|
|
|
|
2018-11-13 00:05:02 +01:00
|
|
|
if (HB_DIRECTION_IS_HORIZONTAL (planner->props.direction))
|
2013-02-15 12:22:26 +01:00
|
|
|
for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++)
|
2018-10-02 14:45:09 +02:00
|
|
|
map->add_feature (horizontal_features[i]);
|
2011-05-31 21:18:13 +02:00
|
|
|
else
|
2015-07-23 12:32:59 +02:00
|
|
|
{
|
2015-07-23 12:52:11 +02:00
|
|
|
/* We really want to find a 'vert' feature if there's any in the font, no
|
|
|
|
* matter which script/langsys it is listed (or not) under.
|
|
|
|
* See various bugs referenced from:
|
2017-11-20 20:49:22 +01:00
|
|
|
* https://github.com/harfbuzz/harfbuzz/issues/63 */
|
2018-10-02 14:48:39 +02:00
|
|
|
map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH);
|
2015-07-23 12:32:59 +02:00
|
|
|
}
|
2011-05-31 21:18:13 +02:00
|
|
|
|
2018-09-10 16:24:52 +02:00
|
|
|
for (unsigned int i = 0; i < num_user_features; i++)
|
|
|
|
{
|
2010-10-12 22:00:21 +02:00
|
|
|
const hb_feature_t *feature = &user_features[i];
|
2018-09-25 00:01:53 +02:00
|
|
|
map->add_feature (feature->tag,
|
2018-09-25 00:11:59 +02:00
|
|
|
(feature->start == HB_FEATURE_GLOBAL_START &&
|
|
|
|
feature->end == HB_FEATURE_GLOBAL_END) ? F_GLOBAL : F_NONE,
|
2018-09-25 00:01:53 +02:00
|
|
|
feature->value);
|
2018-10-23 23:31:51 +02:00
|
|
|
}
|
|
|
|
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-10-23 23:31:51 +02:00
|
|
|
if (planner->apply_morx)
|
|
|
|
{
|
|
|
|
hb_aat_map_builder_t *aat_map = &planner->aat_map;
|
|
|
|
for (unsigned int i = 0; i < num_user_features; i++)
|
|
|
|
{
|
|
|
|
const hb_feature_t *feature = &user_features[i];
|
|
|
|
aat_map->add_feature (feature->tag, feature->value);
|
|
|
|
}
|
2010-05-29 02:37:06 +02:00
|
|
|
}
|
2019-06-18 23:25:52 +02:00
|
|
|
#endif
|
2018-11-29 18:32:47 +01:00
|
|
|
|
|
|
|
if (planner->shaper->override_features)
|
|
|
|
planner->shaper->override_features (planner);
|
2010-10-08 18:29:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-27 08:29:32 +02:00
|
|
|
/*
|
|
|
|
* shaper face data
|
|
|
|
*/
|
|
|
|
|
2018-11-06 04:39:50 +01:00
|
|
|
struct hb_ot_face_data_t {};
|
|
|
|
|
2018-08-02 03:03:32 +02:00
|
|
|
hb_ot_face_data_t *
|
2012-07-27 08:29:32 +02:00
|
|
|
_hb_ot_shaper_face_data_create (hb_face_t *face)
|
|
|
|
{
|
2018-11-06 04:39:50 +01:00
|
|
|
return (hb_ot_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
|
2012-07-27 08:29:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-08-02 03:03:32 +02:00
|
|
|
_hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data)
|
2012-07-27 08:29:32 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* shaper font data
|
|
|
|
*/
|
|
|
|
|
2018-08-02 03:03:32 +02:00
|
|
|
struct hb_ot_font_data_t {};
|
2012-07-27 08:29:32 +02:00
|
|
|
|
2018-08-02 03:03:32 +02:00
|
|
|
hb_ot_font_data_t *
|
2016-02-22 08:00:59 +01:00
|
|
|
_hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED)
|
2012-07-27 08:29:32 +02:00
|
|
|
{
|
2018-08-02 03:03:32 +02:00
|
|
|
return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
|
2012-07-27 08:29:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-10-27 06:01:11 +02:00
|
|
|
_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED)
|
2012-07-27 08:29:32 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* shaper
|
|
|
|
*/
|
|
|
|
|
2012-04-15 02:23:58 +02:00
|
|
|
struct hb_ot_shape_context_t
|
|
|
|
{
|
|
|
|
hb_ot_shape_plan_t *plan;
|
|
|
|
hb_font_t *font;
|
|
|
|
hb_face_t *face;
|
|
|
|
hb_buffer_t *buffer;
|
|
|
|
const hb_feature_t *user_features;
|
|
|
|
unsigned int num_user_features;
|
|
|
|
|
|
|
|
/* Transient stuff */
|
|
|
|
hb_direction_t target_direction;
|
|
|
|
};
|
|
|
|
|
2009-12-20 20:58:26 +01:00
|
|
|
|
|
|
|
|
2010-05-21 15:34:23 +02:00
|
|
|
/* Main shaper */
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
|
2010-05-21 15:34:23 +02:00
|
|
|
/* Prepare */
|
|
|
|
|
2012-04-15 02:23:58 +02:00
|
|
|
static void
|
|
|
|
hb_set_unicode_props (hb_buffer_t *buffer)
|
2010-11-03 21:37:24 +01:00
|
|
|
{
|
2018-10-03 19:44:15 +02:00
|
|
|
/* Implement enough of Unicode Graphemes here that shaping
|
|
|
|
* in reverse-direction wouldn't break graphemes. Namely,
|
|
|
|
* we mark all marks and ZWJ and ZWJ,Extended_Pictographic
|
|
|
|
* sequences as continuations. The foreach_grapheme()
|
|
|
|
* macro uses this bit.
|
|
|
|
*
|
|
|
|
* https://www.unicode.org/reports/tr29/#Regex_Definitions
|
|
|
|
*/
|
2011-07-21 17:34:59 +02:00
|
|
|
unsigned int count = buffer->len;
|
2014-07-17 20:22:11 +02:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2012-04-12 16:06:52 +02:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2018-10-03 19:44:15 +02:00
|
|
|
{
|
2015-11-05 03:46:22 +01:00
|
|
|
_hb_glyph_info_set_unicode_props (&info[i], buffer);
|
2018-10-03 19:44:15 +02:00
|
|
|
|
|
|
|
/* Marks are already set as continuation by the above line.
|
2018-10-03 21:17:59 +02:00
|
|
|
* Handle Emoji_Modifier and ZWJ-continuation. */
|
|
|
|
if (unlikely (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL &&
|
|
|
|
hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x1F3FBu, 0x1F3FFu)))
|
|
|
|
{
|
2021-06-10 03:06:31 +02:00
|
|
|
_hb_glyph_info_set_continuation (&info[i]);
|
|
|
|
}
|
|
|
|
/* Regional_Indicators are hairy as hell...
|
|
|
|
* https://github.com/harfbuzz/harfbuzz/issues/2265 */
|
|
|
|
else if (unlikely (i && hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x1F1E6u, 0x1F1FFu)))
|
|
|
|
{
|
|
|
|
if (hb_in_range<hb_codepoint_t> (info[i - 1].codepoint, 0x1F1E6u, 0x1F1FFu) &&
|
|
|
|
!_hb_glyph_info_is_continuation (&info[i - 1]))
|
2018-10-03 21:17:59 +02:00
|
|
|
_hb_glyph_info_set_continuation (&info[i]);
|
|
|
|
}
|
2019-06-26 23:57:48 +02:00
|
|
|
#ifndef HB_NO_EMOJI_SEQUENCES
|
2018-10-03 21:17:59 +02:00
|
|
|
else if (unlikely (_hb_glyph_info_is_zwj (&info[i])))
|
2018-10-03 19:44:15 +02:00
|
|
|
{
|
|
|
|
_hb_glyph_info_set_continuation (&info[i]);
|
|
|
|
if (i + 1 < count &&
|
|
|
|
_hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint))
|
|
|
|
{
|
2019-08-24 15:27:14 +02:00
|
|
|
i++;
|
2018-10-03 19:44:15 +02:00
|
|
|
_hb_glyph_info_set_unicode_props (&info[i], buffer);
|
|
|
|
_hb_glyph_info_set_continuation (&info[i]);
|
|
|
|
}
|
|
|
|
}
|
2019-06-26 23:29:39 +02:00
|
|
|
#endif
|
2019-01-24 16:08:33 +01:00
|
|
|
/* Or part of the Other_Grapheme_Extend that is not marks.
|
|
|
|
* As of Unicode 11 that is just:
|
|
|
|
*
|
|
|
|
* 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER
|
|
|
|
* FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
|
|
|
|
* E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG
|
|
|
|
*
|
2019-01-24 16:12:52 +01:00
|
|
|
* ZWNJ is special, we don't want to merge it as there's no need, and keeping
|
|
|
|
* it separate results in more granular clusters. Ignore Katakana for now.
|
2019-01-24 16:08:33 +01:00
|
|
|
* Tags are used for Emoji sub-region flag sequences:
|
|
|
|
* https://github.com/harfbuzz/harfbuzz/issues/1556
|
|
|
|
*/
|
|
|
|
else if (unlikely (hb_in_range<hb_codepoint_t> (info[i].codepoint, 0xE0020u, 0xE007Fu)))
|
|
|
|
_hb_glyph_info_set_continuation (&info[i]);
|
2018-10-03 19:44:15 +02:00
|
|
|
}
|
2010-11-03 21:37:24 +01:00
|
|
|
}
|
|
|
|
|
2012-09-02 02:38:45 +02:00
|
|
|
static void
|
|
|
|
hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font)
|
|
|
|
{
|
2019-02-09 11:55:27 +01:00
|
|
|
if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
|
|
|
|
return;
|
|
|
|
|
2012-11-13 23:42:35 +01:00
|
|
|
if (!(buffer->flags & HB_BUFFER_FLAG_BOT) ||
|
Make it easier to use HB_BUFFER_FLAG_BOT/EOT
Previously, we expected users to provide BOT/EOT flags when the
text *segment* was at paragraph boundaries. This meant that for
clients that provide full paragraph to HarfBuzz (eg. Pango), they
had code like this:
hb_buffer_set_flags (hb_buffer,
(item_offset == 0 ? HB_BUFFER_FLAG_BOT : 0) |
(item_offset + item_length == paragraph_length ?
HB_BUFFER_FLAG_EOT : 0));
hb_buffer_add_utf8 (hb_buffer,
paragraph_text, paragraph_length,
item_offset, item_length);
After this change such clients can simply say:
hb_buffer_set_flags (hb_buffer,
HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
hb_buffer_add_utf8 (hb_buffer,
paragraph_text, paragraph_length,
item_offset, item_length);
Ie, HarfBuzz itself checks whether the segment is at the beginning/end
of the paragraph. Clients that only pass item-at-a-time to HarfBuzz
continue not setting any flags whatsoever.
Another way to put it is: if there's pre-context text in the buffer,
HarfBuzz ignores the BOT flag. If there's post-context, it ignores
EOT flag.
2014-08-02 22:17:44 +02:00
|
|
|
buffer->context_len[0] ||
|
2018-10-03 19:19:51 +02:00
|
|
|
!_hb_glyph_info_is_unicode_mark (&buffer->info[0]))
|
2012-09-02 02:38:45 +02:00
|
|
|
return;
|
|
|
|
|
2014-07-11 20:54:42 +02:00
|
|
|
if (!font->has_glyph (0x25CCu))
|
2012-09-02 02:38:45 +02:00
|
|
|
return;
|
|
|
|
|
2014-07-27 00:44:15 +02:00
|
|
|
hb_glyph_info_t dottedcircle = {0};
|
2014-07-11 20:54:42 +02:00
|
|
|
dottedcircle.codepoint = 0x25CCu;
|
2015-11-05 03:46:22 +01:00
|
|
|
_hb_glyph_info_set_unicode_props (&dottedcircle, buffer);
|
2012-09-02 02:38:45 +02:00
|
|
|
|
|
|
|
buffer->clear_output ();
|
|
|
|
|
|
|
|
buffer->idx = 0;
|
|
|
|
hb_glyph_info_t info = dottedcircle;
|
|
|
|
info.cluster = buffer->cur().cluster;
|
|
|
|
info.mask = buffer->cur().mask;
|
2021-03-15 21:12:10 +01:00
|
|
|
(void) buffer->output_info (info);
|
2021-07-13 00:52:31 +02:00
|
|
|
|
2012-09-02 02:38:45 +02:00
|
|
|
buffer->swap_buffers ();
|
|
|
|
}
|
|
|
|
|
2010-05-21 15:34:23 +02:00
|
|
|
static void
|
2011-07-21 17:34:59 +02:00
|
|
|
hb_form_clusters (hb_buffer_t *buffer)
|
2010-05-21 15:34:23 +02:00
|
|
|
{
|
2017-09-05 05:04:59 +02:00
|
|
|
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
|
2015-07-22 17:51:12 +02:00
|
|
|
return;
|
|
|
|
|
2017-09-05 05:04:59 +02:00
|
|
|
if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
|
2018-10-03 21:02:16 +02:00
|
|
|
foreach_grapheme (buffer, start, end)
|
|
|
|
buffer->merge_clusters (start, end);
|
2017-09-05 05:04:59 +02:00
|
|
|
else
|
2018-10-03 21:02:16 +02:00
|
|
|
foreach_grapheme (buffer, start, end)
|
|
|
|
buffer->unsafe_to_break (start, end);
|
2010-05-21 15:34:23 +02:00
|
|
|
}
|
|
|
|
|
2010-10-06 05:00:05 +02:00
|
|
|
static void
|
2011-07-21 17:34:59 +02:00
|
|
|
hb_ensure_native_direction (hb_buffer_t *buffer)
|
2010-05-21 15:34:23 +02:00
|
|
|
{
|
2011-07-21 17:34:59 +02:00
|
|
|
hb_direction_t direction = buffer->props.direction;
|
2018-05-07 22:58:32 +02:00
|
|
|
hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script);
|
2010-05-21 15:34:23 +02:00
|
|
|
|
2021-06-23 17:39:23 +02:00
|
|
|
/* Numeric runs in natively-RTL scripts are actually native-LTR, so we reset
|
|
|
|
* the horiz_dir if the run contains at least one decimal-number char, and no
|
|
|
|
* letter chars (ideally we should be checking for chars with strong
|
|
|
|
* directionality but hb-unicode currently lacks bidi categories).
|
|
|
|
*
|
|
|
|
* This allows digit sequences in Arabic etc to be shaped in "native"
|
|
|
|
* direction, so that features like ligatures will work as intended.
|
|
|
|
*
|
|
|
|
* https://github.com/harfbuzz/harfbuzz/issues/501
|
|
|
|
*/
|
|
|
|
if (unlikely (horiz_dir == HB_DIRECTION_RTL && direction == HB_DIRECTION_LTR))
|
|
|
|
{
|
|
|
|
bool found_number = false, found_letter = false;
|
|
|
|
const auto* info = buffer->info;
|
2021-06-26 16:52:53 +02:00
|
|
|
const auto count = buffer->len;
|
|
|
|
for (unsigned i = 0; i < count; i++)
|
2021-06-23 17:39:23 +02:00
|
|
|
{
|
2021-06-26 16:52:53 +02:00
|
|
|
auto gc = _hb_glyph_info_get_general_category (&info[i]);
|
2021-06-23 17:39:23 +02:00
|
|
|
if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
|
|
|
|
found_number = true;
|
|
|
|
else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc))
|
|
|
|
{
|
|
|
|
found_letter = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found_number && !found_letter)
|
|
|
|
horiz_dir = HB_DIRECTION_LTR;
|
|
|
|
}
|
|
|
|
|
2011-05-25 03:04:15 +02:00
|
|
|
/* TODO vertical:
|
|
|
|
* The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
|
|
|
|
* Ogham fonts are supposed to be implemented BTT or not. Need to research that
|
|
|
|
* first. */
|
2018-05-07 22:58:32 +02:00
|
|
|
if ((HB_DIRECTION_IS_HORIZONTAL (direction) &&
|
|
|
|
direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) ||
|
|
|
|
(HB_DIRECTION_IS_VERTICAL (direction) &&
|
|
|
|
direction != HB_DIRECTION_TTB))
|
2010-05-21 15:34:23 +02:00
|
|
|
{
|
2015-09-01 17:23:40 +02:00
|
|
|
|
2015-07-22 17:51:12 +02:00
|
|
|
if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
|
2018-10-03 21:02:16 +02:00
|
|
|
foreach_grapheme (buffer, start, end)
|
|
|
|
{
|
|
|
|
buffer->merge_clusters (start, end);
|
|
|
|
buffer->reverse_range (start, end);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
foreach_grapheme (buffer, start, end)
|
|
|
|
/* form_clusters() merged clusters already, we don't merge. */
|
|
|
|
buffer->reverse_range (start, end);
|
2015-07-22 16:49:08 +02:00
|
|
|
|
|
|
|
buffer->reverse ();
|
|
|
|
|
2011-07-21 17:34:59 +02:00
|
|
|
buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
|
2010-05-21 15:34:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-07 23:19:21 +01:00
|
|
|
/*
|
|
|
|
* Substitute
|
|
|
|
*/
|
2010-05-21 15:34:23 +02:00
|
|
|
|
2019-12-05 16:28:42 +01:00
|
|
|
static hb_codepoint_t
|
|
|
|
hb_vert_char_for (hb_codepoint_t u)
|
2010-05-21 15:34:23 +02:00
|
|
|
{
|
2019-12-05 16:28:42 +01:00
|
|
|
switch (u >> 8)
|
|
|
|
{
|
|
|
|
case 0x20: switch (u) {
|
|
|
|
case 0x2013u: return 0xfe32u; // EN DASH
|
|
|
|
case 0x2014u: return 0xfe31u; // EM DASH
|
|
|
|
case 0x2025u: return 0xfe30u; // TWO DOT LEADER
|
|
|
|
case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS
|
|
|
|
} break;
|
|
|
|
case 0x30: switch (u) {
|
|
|
|
case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA
|
|
|
|
case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP
|
|
|
|
case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET
|
|
|
|
case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET
|
|
|
|
case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET
|
|
|
|
case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET
|
|
|
|
case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET
|
|
|
|
case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET
|
|
|
|
case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET
|
|
|
|
case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET
|
|
|
|
case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET
|
|
|
|
case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET
|
|
|
|
case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET
|
|
|
|
case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET
|
|
|
|
case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET
|
|
|
|
case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET
|
|
|
|
} break;
|
|
|
|
case 0xfe: switch (u) {
|
|
|
|
case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE
|
|
|
|
} break;
|
|
|
|
case 0xff: switch (u) {
|
|
|
|
case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK
|
|
|
|
case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS
|
|
|
|
case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS
|
|
|
|
case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA
|
|
|
|
case 0xff1au: return 0xfe13u; // FULLWIDTH COLON
|
|
|
|
case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON
|
|
|
|
case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK
|
|
|
|
case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET
|
|
|
|
case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET
|
|
|
|
case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE
|
|
|
|
case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET
|
|
|
|
case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET
|
|
|
|
} break;
|
|
|
|
}
|
2010-05-21 15:34:23 +02:00
|
|
|
|
2019-12-05 16:28:42 +01:00
|
|
|
return u;
|
|
|
|
}
|
2010-07-23 23:22:11 +02:00
|
|
|
|
2019-12-05 16:28:42 +01:00
|
|
|
static inline void
|
|
|
|
hb_ot_rotate_chars (const hb_ot_shape_context_t *c)
|
|
|
|
{
|
|
|
|
hb_buffer_t *buffer = c->buffer;
|
2013-10-18 19:33:09 +02:00
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2019-12-05 16:28:42 +01:00
|
|
|
|
|
|
|
if (HB_DIRECTION_IS_BACKWARD (c->target_direction))
|
|
|
|
{
|
|
|
|
hb_unicode_funcs_t *unicode = buffer->unicode;
|
|
|
|
hb_mask_t rtlm_mask = c->plan->rtlm_mask;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < count; i++) {
|
|
|
|
hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint);
|
|
|
|
if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint)))
|
|
|
|
info[i].codepoint = codepoint;
|
|
|
|
else
|
|
|
|
info[i].mask |= rtlm_mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert)
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < count; i++) {
|
|
|
|
hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint);
|
|
|
|
if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint)))
|
|
|
|
info[i].codepoint = codepoint;
|
|
|
|
}
|
2010-05-21 15:34:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-22 22:17:54 +01:00
|
|
|
static inline void
|
2018-10-08 22:32:44 +02:00
|
|
|
hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c)
|
2013-12-22 22:17:54 +01:00
|
|
|
{
|
2019-06-20 01:57:48 +02:00
|
|
|
#ifdef HB_NO_OT_SHAPE_FRACTIONS
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
2015-11-05 03:58:02 +01:00
|
|
|
if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
|
|
|
|
!c->plan->has_frac)
|
2013-12-23 02:48:53 +01:00
|
|
|
return;
|
|
|
|
|
2013-12-22 22:17:54 +01:00
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
|
2017-01-18 21:48:13 +01:00
|
|
|
hb_mask_t pre_mask, post_mask;
|
|
|
|
if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
|
|
|
|
{
|
|
|
|
pre_mask = c->plan->numr_mask | c->plan->frac_mask;
|
|
|
|
post_mask = c->plan->frac_mask | c->plan->dnom_mask;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pre_mask = c->plan->frac_mask | c->plan->dnom_mask;
|
|
|
|
post_mask = c->plan->numr_mask | c->plan->frac_mask;
|
|
|
|
}
|
|
|
|
|
2013-12-22 22:17:54 +01:00
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
2014-07-11 20:54:42 +02:00
|
|
|
if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */
|
2013-12-22 22:17:54 +01:00
|
|
|
{
|
|
|
|
unsigned int start = i, end = i + 1;
|
|
|
|
while (start &&
|
|
|
|
_hb_glyph_info_get_general_category (&info[start - 1]) ==
|
|
|
|
HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
|
2019-08-24 15:27:14 +02:00
|
|
|
start--;
|
2013-12-22 22:17:54 +01:00
|
|
|
while (end < count &&
|
|
|
|
_hb_glyph_info_get_general_category (&info[end]) ==
|
|
|
|
HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
|
2019-08-24 15:27:14 +02:00
|
|
|
end++;
|
2013-12-22 22:17:54 +01:00
|
|
|
|
2017-08-31 02:28:22 +02:00
|
|
|
buffer->unsafe_to_break (start, end);
|
|
|
|
|
2013-12-23 01:33:35 +01:00
|
|
|
for (unsigned int j = start; j < i; j++)
|
2019-08-24 15:27:14 +02:00
|
|
|
info[j].mask |= pre_mask;
|
2013-12-23 02:48:53 +01:00
|
|
|
info[i].mask |= c->plan->frac_mask;
|
2013-12-23 01:33:35 +01:00
|
|
|
for (unsigned int j = i + 1; j < end; j++)
|
2019-08-24 15:27:14 +02:00
|
|
|
info[j].mask |= post_mask;
|
2013-12-22 22:17:54 +01:00
|
|
|
|
|
|
|
i = end - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
static inline void
|
2018-10-08 22:32:44 +02:00
|
|
|
hb_ot_shape_initialize_masks (const hb_ot_shape_context_t *c)
|
2012-08-10 03:58:07 +02:00
|
|
|
{
|
|
|
|
hb_ot_map_t *map = &c->plan->map;
|
2013-10-18 19:33:09 +02:00
|
|
|
hb_buffer_t *buffer = c->buffer;
|
2012-08-10 03:58:07 +02:00
|
|
|
|
|
|
|
hb_mask_t global_mask = map->get_global_mask ();
|
2013-10-18 19:33:09 +02:00
|
|
|
buffer->reset_masks (global_mask);
|
2013-12-21 06:18:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
2018-10-08 22:32:44 +02:00
|
|
|
hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c)
|
2013-12-21 06:18:18 +01:00
|
|
|
{
|
|
|
|
hb_ot_map_t *map = &c->plan->map;
|
|
|
|
hb_buffer_t *buffer = c->buffer;
|
2012-08-10 03:58:07 +02:00
|
|
|
|
2013-12-22 22:17:54 +01:00
|
|
|
hb_ot_shape_setup_masks_fraction (c);
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
if (c->plan->shaper->setup_masks)
|
2013-10-18 19:33:09 +02:00
|
|
|
c->plan->shaper->setup_masks (c->plan, buffer, c->font);
|
2012-08-10 03:58:07 +02:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < c->num_user_features; i++)
|
|
|
|
{
|
|
|
|
const hb_feature_t *feature = &c->user_features[i];
|
2019-12-15 15:50:01 +01:00
|
|
|
if (!(feature->start == HB_FEATURE_GLOBAL_START && feature->end == HB_FEATURE_GLOBAL_END)) {
|
2012-08-10 03:58:07 +02:00
|
|
|
unsigned int shift;
|
|
|
|
hb_mask_t mask = map->get_mask (feature->tag, &shift);
|
2013-10-18 19:33:09 +02:00
|
|
|
buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
|
2012-08-10 03:58:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-22 19:41:10 +02:00
|
|
|
static void
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer)
|
2015-07-22 19:41:10 +02:00
|
|
|
{
|
2015-11-05 03:46:22 +01:00
|
|
|
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
|
2018-01-10 03:35:20 +01:00
|
|
|
(buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) ||
|
|
|
|
(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES))
|
2015-07-22 19:41:10 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
hb_glyph_position_t *pos = buffer->pos;
|
|
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i])))
|
|
|
|
pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0;
|
|
|
|
}
|
2015-07-22 18:36:23 +02:00
|
|
|
|
|
|
|
static void
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_ot_hide_default_ignorables (hb_buffer_t *buffer,
|
|
|
|
hb_font_t *font)
|
2015-07-22 18:36:23 +02:00
|
|
|
{
|
2015-11-05 03:46:22 +01:00
|
|
|
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
|
|
|
|
(buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES))
|
2015-07-22 18:36:23 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_codepoint_t invisible = buffer->invisible;
|
2018-01-10 03:35:20 +01:00
|
|
|
if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) &&
|
2018-11-07 23:19:21 +01:00
|
|
|
(invisible || font->get_nominal_glyph (' ', &invisible)))
|
2015-07-22 18:36:23 +02:00
|
|
|
{
|
2018-10-07 18:41:52 +02:00
|
|
|
/* Replace default-ignorables with a zero-advance invisible glyph. */
|
2018-10-08 00:52:12 +02:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2015-07-22 18:36:23 +02:00
|
|
|
{
|
2015-07-22 18:41:31 +02:00
|
|
|
if (_hb_glyph_info_is_default_ignorable (&info[i]))
|
2018-10-07 18:41:52 +02:00
|
|
|
info[i].codepoint = invisible;
|
2015-07-22 18:36:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_ot_layout_delete_glyphs_inplace (buffer, _hb_glyph_info_is_default_ignorable);
|
2015-07-22 18:36:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
static inline void
|
2012-08-10 04:33:32 +02:00
|
|
|
hb_ot_map_glyphs_fast (hb_buffer_t *buffer)
|
2010-05-21 15:34:23 +02:00
|
|
|
{
|
2012-08-10 04:33:32 +02:00
|
|
|
/* Normalization process sets up glyph_index(), we just copy it. */
|
|
|
|
unsigned int count = buffer->len;
|
2014-07-17 20:22:11 +02:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2012-08-10 04:33:32 +02:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2014-07-17 20:22:11 +02:00
|
|
|
info[i].codepoint = info[i].glyph_index();
|
2015-08-18 19:42:47 +02:00
|
|
|
|
|
|
|
buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
|
2010-05-21 15:34:23 +02:00
|
|
|
}
|
|
|
|
|
2016-12-22 20:33:54 +01:00
|
|
|
static inline void
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_synthesize_glyph_classes (hb_buffer_t *buffer)
|
2016-12-22 20:33:54 +01:00
|
|
|
{
|
2018-11-07 23:19:21 +01:00
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2016-12-22 20:33:54 +01:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
hb_ot_layout_glyph_props_flags_t klass;
|
|
|
|
|
|
|
|
/* Never mark default-ignorables as marks.
|
|
|
|
* They won't get in the way of lookups anyway,
|
|
|
|
* but having them as mark will cause them to be skipped
|
|
|
|
* over if the lookup-flag says so, but at least for the
|
|
|
|
* Mongolian variation selectors, looks like Uniscribe
|
|
|
|
* marks them as non-mark. Some Mongolian fonts without
|
|
|
|
* GDEF rely on this. Another notable character that
|
|
|
|
* this applies to is COMBINING GRAPHEME JOINER. */
|
|
|
|
klass = (_hb_glyph_info_get_general_category (&info[i]) !=
|
|
|
|
HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ||
|
|
|
|
_hb_glyph_info_is_default_ignorable (&info[i])) ?
|
|
|
|
HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH :
|
|
|
|
HB_OT_LAYOUT_GLYPH_PROPS_MARK;
|
|
|
|
_hb_glyph_info_set_glyph_props (&info[i], klass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
static inline void
|
2018-10-08 22:32:44 +02:00
|
|
|
hb_ot_substitute_default (const hb_ot_shape_context_t *c)
|
2010-05-21 15:34:23 +02:00
|
|
|
{
|
2013-10-18 19:33:09 +02:00
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
|
2019-12-05 16:28:42 +01:00
|
|
|
hb_ot_rotate_chars (c);
|
2012-08-10 03:58:07 +02:00
|
|
|
|
2013-10-18 19:33:09 +02:00
|
|
|
HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index);
|
2012-08-10 04:33:32 +02:00
|
|
|
|
2020-09-18 17:37:22 +02:00
|
|
|
_hb_ot_shape_normalize (c->plan, buffer, c->font);
|
2012-08-10 03:58:07 +02:00
|
|
|
|
|
|
|
hb_ot_shape_setup_masks (c);
|
2011-07-28 22:48:43 +02:00
|
|
|
|
2012-09-02 01:20:41 +02:00
|
|
|
/* This is unfortunate to go here, but necessary... */
|
2018-10-09 05:30:24 +02:00
|
|
|
if (c->plan->fallback_mark_positioning)
|
|
|
|
_hb_ot_shape_fallback_mark_position_recategorize_marks (c->plan, c->font, buffer);
|
2012-09-02 01:20:41 +02:00
|
|
|
|
2013-10-18 19:33:09 +02:00
|
|
|
hb_ot_map_glyphs_fast (buffer);
|
2012-08-10 04:33:32 +02:00
|
|
|
|
2013-10-18 19:33:09 +02:00
|
|
|
HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index);
|
2010-05-21 15:34:23 +02:00
|
|
|
}
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
static inline void
|
2018-10-08 22:32:44 +02:00
|
|
|
hb_ot_substitute_complex (const hb_ot_shape_context_t *c)
|
2011-07-25 06:44:50 +02:00
|
|
|
{
|
2013-10-18 19:33:09 +02:00
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
|
|
|
|
hb_ot_layout_substitute_start (c->font, buffer);
|
2012-07-31 01:30:01 +02:00
|
|
|
|
2018-10-02 13:27:11 +02:00
|
|
|
if (c->plan->fallback_glyph_classes)
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_synthesize_glyph_classes (c->buffer);
|
2016-12-22 20:33:54 +01:00
|
|
|
|
2018-11-14 20:49:34 +01:00
|
|
|
c->plan->substitute (c->font, buffer);
|
2011-07-25 06:44:50 +02:00
|
|
|
}
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
static inline void
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_ot_substitute_pre (const hb_ot_shape_context_t *c)
|
2012-08-10 03:58:07 +02:00
|
|
|
{
|
|
|
|
hb_ot_substitute_default (c);
|
2015-11-05 07:28:44 +01:00
|
|
|
|
|
|
|
_hb_buffer_allocate_gsubgpos_vars (c->buffer);
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
hb_ot_substitute_complex (c);
|
|
|
|
}
|
2010-05-21 15:34:23 +02:00
|
|
|
|
2018-11-07 23:19:21 +01:00
|
|
|
static inline void
|
|
|
|
hb_ot_substitute_post (const hb_ot_shape_context_t *c)
|
|
|
|
{
|
|
|
|
hb_ot_hide_default_ignorables (c->buffer, c->font);
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-11-07 23:19:21 +01:00
|
|
|
if (c->plan->apply_morx)
|
|
|
|
hb_aat_layout_remove_deleted_glyphs (c->buffer);
|
2019-06-18 23:25:52 +02:00
|
|
|
#endif
|
2018-11-07 23:19:21 +01:00
|
|
|
|
2020-09-17 15:27:31 +02:00
|
|
|
if (c->plan->shaper->postprocess_glyphs &&
|
2020-09-18 17:25:43 +02:00
|
|
|
c->buffer->message(c->font, "start postprocess-glyphs")) {
|
2018-11-07 23:19:21 +01:00
|
|
|
c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
|
2020-09-18 17:28:29 +02:00
|
|
|
(void) c->buffer->message(c->font, "end postprocess-glyphs");
|
2020-09-17 15:27:31 +02:00
|
|
|
}
|
2018-11-07 23:19:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Position
|
|
|
|
*/
|
2010-05-21 15:34:23 +02:00
|
|
|
|
2013-05-20 15:18:52 +02:00
|
|
|
static inline void
|
2014-06-10 14:10:30 +02:00
|
|
|
adjust_mark_offsets (hb_glyph_position_t *pos)
|
|
|
|
{
|
|
|
|
pos->x_offset -= pos->x_advance;
|
|
|
|
pos->y_offset -= pos->y_advance;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
zero_mark_width (hb_glyph_position_t *pos)
|
|
|
|
{
|
|
|
|
pos->x_advance = 0;
|
|
|
|
pos->y_advance = 0;
|
|
|
|
}
|
|
|
|
|
2013-05-20 15:18:52 +02:00
|
|
|
static inline void
|
2014-06-10 14:10:30 +02:00
|
|
|
zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets)
|
2013-05-20 15:18:52 +02:00
|
|
|
{
|
|
|
|
unsigned int count = buffer->len;
|
2014-07-17 20:22:11 +02:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2013-05-20 15:18:52 +02:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2014-07-17 20:22:11 +02:00
|
|
|
if (_hb_glyph_info_is_mark (&info[i]))
|
2013-05-20 15:18:52 +02:00
|
|
|
{
|
2014-06-10 14:10:30 +02:00
|
|
|
if (adjust_offsets)
|
2019-08-24 15:27:14 +02:00
|
|
|
adjust_mark_offsets (&buffer->pos[i]);
|
2014-06-10 14:10:30 +02:00
|
|
|
zero_mark_width (&buffer->pos[i]);
|
2013-05-20 15:18:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
static inline void
|
2018-10-08 22:32:44 +02:00
|
|
|
hb_ot_position_default (const hb_ot_shape_context_t *c)
|
2010-05-21 15:34:23 +02:00
|
|
|
{
|
2013-10-18 19:33:09 +02:00
|
|
|
hb_direction_t direction = c->buffer->props.direction;
|
2010-10-06 05:00:05 +02:00
|
|
|
unsigned int count = c->buffer->len;
|
2013-10-18 19:33:09 +02:00
|
|
|
hb_glyph_info_t *info = c->buffer->info;
|
|
|
|
hb_glyph_position_t *pos = c->buffer->pos;
|
Adjust mark advance-width zeroing logic for Myanmar
Before, we were zeroing advance width of attached marks for
non-Indic scripts, and not doing it for Indic.
We have now three different behaviors, which seem to better
reflect what Uniscribe is doing:
- For Indic, no explicit zeroing happens whatsoever, which
is the same as before,
- For Myanmar, zero advance width of glyphs marked as marks
*in GDEF*, and do that *before* applying GPOS. This seems
to be what the new Win8 Myanmar shaper does,
- For everything else, zero advance width of glyphs that are
from General_Category=Mn Unicode characters, and do so
before applying GPOS. This seems to be what Uniscribe does
for Latin at least.
With these changes, positioning of all tests matches for Myanmar,
except for the glitch in Uniscribe not applying 'mark'. See preivous
commit.
2013-02-12 15:44:57 +01:00
|
|
|
|
2015-11-05 04:28:17 +01:00
|
|
|
if (HB_DIRECTION_IS_HORIZONTAL (direction))
|
|
|
|
{
|
2018-08-01 06:01:08 +02:00
|
|
|
c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]),
|
2019-08-24 15:27:14 +02:00
|
|
|
&pos[0].x_advance, sizeof(pos[0]));
|
2015-11-27 00:48:42 +01:00
|
|
|
/* The nil glyph_h_origin() func returns 0, so no need to apply it. */
|
2015-11-05 06:53:16 +01:00
|
|
|
if (c->font->has_glyph_h_origin_func ())
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
c->font->subtract_glyph_h_origin (info[i].codepoint,
|
|
|
|
&pos[i].x_offset,
|
|
|
|
&pos[i].y_offset);
|
2015-11-05 04:28:17 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-08-06 18:45:17 +02:00
|
|
|
c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]),
|
2019-08-24 15:27:14 +02:00
|
|
|
&pos[0].y_advance, sizeof(pos[0]));
|
2015-11-05 04:28:17 +01:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2015-11-27 00:48:42 +01:00
|
|
|
{
|
|
|
|
c->font->subtract_glyph_v_origin (info[i].codepoint,
|
|
|
|
&pos[i].x_offset,
|
|
|
|
&pos[i].y_offset);
|
|
|
|
}
|
Adjust mark advance-width zeroing logic for Myanmar
Before, we were zeroing advance width of attached marks for
non-Indic scripts, and not doing it for Indic.
We have now three different behaviors, which seem to better
reflect what Uniscribe is doing:
- For Indic, no explicit zeroing happens whatsoever, which
is the same as before,
- For Myanmar, zero advance width of glyphs marked as marks
*in GDEF*, and do that *before* applying GPOS. This seems
to be what the new Win8 Myanmar shaper does,
- For everything else, zero advance width of glyphs that are
from General_Category=Mn Unicode characters, and do so
before applying GPOS. This seems to be what Uniscribe does
for Latin at least.
With these changes, positioning of all tests matches for Myanmar,
except for the glitch in Uniscribe not applying 'mark'. See preivous
commit.
2013-02-12 15:44:57 +01:00
|
|
|
}
|
2015-11-05 02:27:07 +01:00
|
|
|
if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK)
|
|
|
|
_hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer);
|
2013-09-14 02:17:42 +02:00
|
|
|
}
|
|
|
|
|
2016-12-22 21:40:19 +01:00
|
|
|
static inline void
|
2018-10-08 22:32:44 +02:00
|
|
|
hb_ot_position_complex (const hb_ot_shape_context_t *c)
|
2013-09-14 02:17:42 +02:00
|
|
|
{
|
|
|
|
unsigned int count = c->buffer->len;
|
2017-10-03 17:22:43 +02:00
|
|
|
hb_glyph_info_t *info = c->buffer->info;
|
|
|
|
hb_glyph_position_t *pos = c->buffer->pos;
|
2016-02-11 10:27:41 +01:00
|
|
|
|
2018-11-22 21:52:29 +01:00
|
|
|
/* If the font has no GPOS and direction is forward, then when
|
|
|
|
* zeroing mark widths, we shift the mark with it, such that the
|
|
|
|
* mark is positioned hanging over the previous glyph. When
|
2014-06-10 14:10:30 +02:00
|
|
|
* direction is backward we don't shift and it will end up
|
|
|
|
* hanging over the next glyph after the final reordering.
|
2018-11-22 21:52:29 +01:00
|
|
|
*
|
|
|
|
* Note: If fallback positinoing happens, we don't care about
|
|
|
|
* this as it will be overriden.
|
2014-06-10 14:10:30 +02:00
|
|
|
*/
|
2018-11-22 21:52:29 +01:00
|
|
|
bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing &&
|
2016-12-22 21:40:19 +01:00
|
|
|
HB_DIRECTION_IS_FORWARD (c->buffer->props.direction);
|
Adjust mark advance-width zeroing logic for Myanmar
Before, we were zeroing advance width of attached marks for
non-Indic scripts, and not doing it for Indic.
We have now three different behaviors, which seem to better
reflect what Uniscribe is doing:
- For Indic, no explicit zeroing happens whatsoever, which
is the same as before,
- For Myanmar, zero advance width of glyphs marked as marks
*in GDEF*, and do that *before* applying GPOS. This seems
to be what the new Win8 Myanmar shaper does,
- For everything else, zero advance width of glyphs that are
from General_Category=Mn Unicode characters, and do so
before applying GPOS. This seems to be what Uniscribe does
for Latin at least.
With these changes, positioning of all tests matches for Myanmar,
except for the glitch in Uniscribe not applying 'mark'. See preivous
commit.
2013-02-12 15:44:57 +01:00
|
|
|
|
2017-10-03 17:22:43 +02:00
|
|
|
/* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */
|
|
|
|
|
|
|
|
/* The nil glyph_h_origin() func returns 0, so no need to apply it. */
|
|
|
|
if (c->font->has_glyph_h_origin_func ())
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
c->font->add_glyph_h_origin (info[i].codepoint,
|
|
|
|
&pos[i].x_offset,
|
|
|
|
&pos[i].y_offset);
|
|
|
|
|
|
|
|
hb_ot_layout_position_start (c->font, c->buffer);
|
|
|
|
|
2018-11-07 22:19:51 +01:00
|
|
|
if (c->plan->zero_marks)
|
2018-10-11 03:29:46 +02:00
|
|
|
switch (c->plan->shaper->zero_width_marks)
|
|
|
|
{
|
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
|
|
|
|
zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
|
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
|
|
|
|
break;
|
|
|
|
}
|
2011-07-25 06:44:50 +02:00
|
|
|
|
2018-11-14 20:49:34 +01:00
|
|
|
c->plan->position (c->font, c->buffer);
|
2018-10-11 17:10:06 +02:00
|
|
|
|
2018-11-07 22:19:51 +01:00
|
|
|
if (c->plan->zero_marks)
|
2018-10-11 03:29:46 +02:00
|
|
|
switch (c->plan->shaper->zero_width_marks)
|
|
|
|
{
|
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
|
|
|
|
zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
|
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
|
|
|
|
break;
|
|
|
|
}
|
2013-02-13 11:57:24 +01:00
|
|
|
|
2018-11-07 23:19:21 +01:00
|
|
|
/* Finish off. Has to follow a certain order. */
|
2016-02-11 10:34:28 +01:00
|
|
|
hb_ot_layout_position_finish_advances (c->font, c->buffer);
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_ot_zero_width_default_ignorables (c->buffer);
|
2019-06-26 22:31:01 +02:00
|
|
|
#ifndef HB_NO_AAT_SHAPE
|
2018-11-07 23:19:21 +01:00
|
|
|
if (c->plan->apply_morx)
|
|
|
|
hb_aat_layout_zero_width_deleted_glyphs (c->buffer);
|
2019-06-18 23:25:52 +02:00
|
|
|
#endif
|
2016-02-11 10:34:28 +01:00
|
|
|
hb_ot_layout_position_finish_offsets (c->font, c->buffer);
|
2017-10-03 17:22:43 +02:00
|
|
|
|
|
|
|
/* The nil glyph_h_origin() func returns 0, so no need to apply it. */
|
|
|
|
if (c->font->has_glyph_h_origin_func ())
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
c->font->subtract_glyph_h_origin (info[i].codepoint,
|
|
|
|
&pos[i].x_offset,
|
|
|
|
&pos[i].y_offset);
|
2018-11-07 22:05:36 +01:00
|
|
|
|
2018-11-22 21:52:29 +01:00
|
|
|
if (c->plan->fallback_mark_positioning)
|
2019-01-24 12:08:23 +01:00
|
|
|
_hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer,
|
|
|
|
adjust_offsets_when_zeroing);
|
2011-07-25 06:44:50 +02:00
|
|
|
}
|
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
static inline void
|
2018-10-08 22:32:44 +02:00
|
|
|
hb_ot_position (const hb_ot_shape_context_t *c)
|
2012-08-10 03:58:07 +02:00
|
|
|
{
|
2016-02-11 10:27:41 +01:00
|
|
|
c->buffer->clear_positions ();
|
2013-09-14 02:17:42 +02:00
|
|
|
|
2012-08-10 03:58:07 +02:00
|
|
|
hb_ot_position_default (c);
|
|
|
|
|
2016-12-22 21:40:19 +01:00
|
|
|
hb_ot_position_complex (c);
|
2012-08-10 03:58:07 +02:00
|
|
|
|
|
|
|
if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
|
|
|
|
hb_buffer_reverse (c->buffer);
|
|
|
|
|
2014-08-02 23:18:46 +02:00
|
|
|
_hb_buffer_deallocate_gsubgpos_vars (c->buffer);
|
2010-05-21 15:34:23 +02:00
|
|
|
}
|
|
|
|
|
2017-08-12 04:06:07 +02:00
|
|
|
static inline void
|
|
|
|
hb_propagate_flags (hb_buffer_t *buffer)
|
|
|
|
{
|
|
|
|
/* Propagate cluster-level glyph flags to be the same on all cluster glyphs.
|
|
|
|
* Simplifies using them. */
|
|
|
|
|
|
|
|
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK))
|
|
|
|
return;
|
|
|
|
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
|
|
|
|
foreach_cluster (buffer, start, end)
|
|
|
|
{
|
|
|
|
unsigned int mask = 0;
|
|
|
|
for (unsigned int i = start; i < end; i++)
|
|
|
|
if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
|
|
|
|
{
|
|
|
|
mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (mask)
|
|
|
|
for (unsigned int i = start; i < end; i++)
|
|
|
|
info[i].mask |= mask;
|
|
|
|
}
|
|
|
|
}
|
2012-08-10 03:58:07 +02:00
|
|
|
|
|
|
|
/* Pull it all together! */
|
2010-05-21 15:34:23 +02:00
|
|
|
|
2010-10-06 05:00:05 +02:00
|
|
|
static void
|
2012-07-27 08:29:32 +02:00
|
|
|
hb_ot_shape_internal (hb_ot_shape_context_t *c)
|
2010-05-21 15:34:23 +02:00
|
|
|
{
|
2011-07-28 22:48:43 +02:00
|
|
|
c->buffer->deallocate_var_all ();
|
2015-11-05 02:27:07 +01:00
|
|
|
c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
|
2018-07-10 14:12:37 +02:00
|
|
|
if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR)))
|
2015-11-06 08:44:59 +01:00
|
|
|
{
|
2019-05-08 05:54:31 +02:00
|
|
|
c->buffer->max_len = hb_max (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR,
|
2020-07-18 19:44:52 +02:00
|
|
|
(unsigned) HB_BUFFER_MAX_LEN_MIN);
|
2015-11-06 08:44:59 +01:00
|
|
|
}
|
2018-07-10 14:12:37 +02:00
|
|
|
if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR)))
|
2017-11-15 06:53:48 +01:00
|
|
|
{
|
2019-05-08 05:54:31 +02:00
|
|
|
c->buffer->max_ops = hb_max (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR,
|
2020-07-18 19:44:52 +02:00
|
|
|
(unsigned) HB_BUFFER_MAX_OPS_MIN);
|
2017-11-15 06:53:48 +01:00
|
|
|
}
|
2011-07-28 22:48:43 +02:00
|
|
|
|
2010-10-12 21:35:45 +02:00
|
|
|
/* Save the original direction, we use it later. */
|
2010-12-07 22:22:02 +01:00
|
|
|
c->target_direction = c->buffer->props.direction;
|
2010-10-12 21:35:45 +02:00
|
|
|
|
2013-10-18 00:42:39 +02:00
|
|
|
_hb_buffer_allocate_unicode_vars (c->buffer);
|
2011-07-28 22:48:43 +02:00
|
|
|
|
2017-09-05 05:04:59 +02:00
|
|
|
hb_ot_shape_initialize_masks (c);
|
2012-05-09 15:04:13 +02:00
|
|
|
hb_set_unicode_props (c->buffer);
|
2012-09-02 02:38:45 +02:00
|
|
|
hb_insert_dotted_circle (c->buffer, c->font);
|
2017-09-05 05:04:59 +02:00
|
|
|
|
2011-07-22 22:15:32 +02:00
|
|
|
hb_form_clusters (c->buffer);
|
|
|
|
|
2011-07-21 17:34:59 +02:00
|
|
|
hb_ensure_native_direction (c->buffer);
|
2011-07-21 07:11:09 +02:00
|
|
|
|
2020-09-17 15:27:31 +02:00
|
|
|
if (c->plan->shaper->preprocess_text &&
|
2021-07-13 00:52:31 +02:00
|
|
|
c->buffer->message(c->font, "start preprocess-text"))
|
|
|
|
{
|
2015-11-05 22:24:15 +01:00
|
|
|
c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
|
2020-09-18 17:28:29 +02:00
|
|
|
(void) c->buffer->message(c->font, "end preprocess-text");
|
2020-09-17 15:27:31 +02:00
|
|
|
}
|
2015-11-05 22:24:15 +01:00
|
|
|
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_ot_substitute_pre (c);
|
2012-08-10 03:58:07 +02:00
|
|
|
hb_ot_position (c);
|
2018-11-07 23:19:21 +01:00
|
|
|
hb_ot_substitute_post (c);
|
2015-11-05 22:24:15 +01:00
|
|
|
|
2017-08-12 04:06:07 +02:00
|
|
|
hb_propagate_flags (c->buffer);
|
|
|
|
|
2013-10-18 00:42:39 +02:00
|
|
|
_hb_buffer_deallocate_unicode_vars (c->buffer);
|
2011-07-28 22:48:43 +02:00
|
|
|
|
2010-12-07 22:22:02 +01:00
|
|
|
c->buffer->props.direction = c->target_direction;
|
2011-07-28 23:06:46 +02:00
|
|
|
|
2015-11-06 08:44:59 +01:00
|
|
|
c->buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT;
|
2017-11-15 06:53:48 +01:00
|
|
|
c->buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT;
|
2011-07-28 23:06:46 +02:00
|
|
|
c->buffer->deallocate_var_all ();
|
2010-10-06 05:00:05 +02:00
|
|
|
}
|
|
|
|
|
2010-10-12 22:57:47 +02:00
|
|
|
|
2011-08-05 04:31:05 +02:00
|
|
|
hb_bool_t
|
2012-07-27 04:05:39 +02:00
|
|
|
_hb_ot_shape (hb_shape_plan_t *shape_plan,
|
|
|
|
hb_font_t *font,
|
2012-04-12 20:53:53 +02:00
|
|
|
hb_buffer_t *buffer,
|
|
|
|
const hb_feature_t *features,
|
|
|
|
unsigned int num_features)
|
2010-10-06 05:00:05 +02:00
|
|
|
{
|
2018-11-12 03:32:01 +01:00
|
|
|
hb_ot_shape_context_t c = {&shape_plan->ot, font, font->face, buffer, features, num_features};
|
2012-07-27 08:29:32 +02:00
|
|
|
hb_ot_shape_internal (&c);
|
2011-08-05 04:31:05 +02:00
|
|
|
|
2012-06-06 02:35:40 +02:00
|
|
|
return true;
|
2010-05-21 15:34:23 +02:00
|
|
|
}
|
2012-04-24 22:56:37 +02:00
|
|
|
|
|
|
|
|
2015-06-01 13:22:01 +02:00
|
|
|
/**
|
2015-11-27 01:30:37 +01:00
|
|
|
* hb_ot_shape_plan_collect_lookups:
|
2019-04-22 20:22:24 +02:00
|
|
|
* @shape_plan: #hb_shape_plan_t to query
|
|
|
|
* @table_tag: GSUB or GPOS
|
|
|
|
* @lookup_indexes: (out): The #hb_set_t set of lookups returned
|
|
|
|
*
|
|
|
|
* Computes the complete set of GSUB or GPOS lookups that are applicable
|
2021-05-13 12:39:09 +02:00
|
|
|
* under a given @shape_plan.
|
2015-11-27 01:30:37 +01:00
|
|
|
*
|
2015-06-01 13:22:01 +02:00
|
|
|
* Since: 0.9.7
|
|
|
|
**/
|
2012-11-16 03:39:46 +01:00
|
|
|
void
|
|
|
|
hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
|
|
|
|
hb_tag_t table_tag,
|
|
|
|
hb_set_t *lookup_indexes /* OUT */)
|
|
|
|
{
|
2018-11-12 03:32:01 +01:00
|
|
|
shape_plan->ot.collect_lookups (table_tag, lookup_indexes);
|
2012-11-16 03:39:46 +01:00
|
|
|
}
|
2012-08-10 04:33:32 +02:00
|
|
|
|
2012-11-16 03:39:46 +01:00
|
|
|
|
|
|
|
/* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */
|
|
|
|
static void
|
|
|
|
add_char (hb_font_t *font,
|
|
|
|
hb_unicode_funcs_t *unicode,
|
|
|
|
hb_bool_t mirror,
|
|
|
|
hb_codepoint_t u,
|
|
|
|
hb_set_t *glyphs)
|
2012-08-10 04:33:32 +02:00
|
|
|
{
|
2012-11-16 03:39:46 +01:00
|
|
|
hb_codepoint_t glyph;
|
2016-02-24 11:05:23 +01:00
|
|
|
if (font->get_nominal_glyph (u, &glyph))
|
2012-11-16 03:39:46 +01:00
|
|
|
glyphs->add (glyph);
|
|
|
|
if (mirror)
|
|
|
|
{
|
|
|
|
hb_codepoint_t m = unicode->mirroring (u);
|
2016-02-24 11:05:23 +01:00
|
|
|
if (m != u && font->get_nominal_glyph (m, &glyph))
|
2012-11-16 03:39:46 +01:00
|
|
|
glyphs->add (glyph);
|
|
|
|
}
|
2012-08-10 04:33:32 +02:00
|
|
|
}
|
|
|
|
|
2012-11-16 03:39:46 +01:00
|
|
|
|
2015-06-01 13:22:01 +02:00
|
|
|
/**
|
2015-11-27 01:30:37 +01:00
|
|
|
* hb_ot_shape_glyphs_closure:
|
2019-04-22 20:22:24 +02:00
|
|
|
* @font: #hb_font_t to work upon
|
|
|
|
* @buffer: The input buffer to compute from
|
|
|
|
* @features: (array length=num_features): The features enabled on the buffer
|
|
|
|
* @num_features: The number of features enabled on the buffer
|
|
|
|
* @glyphs: (out): The #hb_set_t set of glyphs comprising the transitive closure of the query
|
|
|
|
*
|
|
|
|
* Computes the transitive closure of glyphs needed for a specified
|
|
|
|
* input buffer under the given font and feature list. The closure is
|
|
|
|
* computed as a set, not as a list.
|
2015-11-27 01:30:37 +01:00
|
|
|
*
|
2015-06-01 13:22:01 +02:00
|
|
|
* Since: 0.9.2
|
|
|
|
**/
|
2012-04-24 22:56:37 +02:00
|
|
|
void
|
|
|
|
hb_ot_shape_glyphs_closure (hb_font_t *font,
|
|
|
|
hb_buffer_t *buffer,
|
|
|
|
const hb_feature_t *features,
|
|
|
|
unsigned int num_features,
|
|
|
|
hb_set_t *glyphs)
|
|
|
|
{
|
2017-10-15 12:11:08 +02:00
|
|
|
const char *shapers[] = {"ot", nullptr};
|
2012-11-16 03:39:46 +01:00
|
|
|
hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
|
|
|
|
features, num_features, shapers);
|
2012-04-24 22:56:37 +02:00
|
|
|
|
2012-11-16 03:39:46 +01:00
|
|
|
bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL;
|
2012-04-24 22:56:37 +02:00
|
|
|
|
|
|
|
unsigned int count = buffer->len;
|
2014-07-17 20:22:11 +02:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2012-04-24 22:56:37 +02:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2014-07-17 20:22:11 +02:00
|
|
|
add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs);
|
2012-11-16 03:39:46 +01:00
|
|
|
|
2017-10-22 23:48:06 +02:00
|
|
|
hb_set_t *lookups = hb_set_create ();
|
|
|
|
hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups);
|
2018-06-07 01:02:51 +02:00
|
|
|
hb_ot_layout_lookups_substitute_closure (font->face, lookups, glyphs);
|
2017-10-22 23:48:06 +02:00
|
|
|
|
|
|
|
hb_set_destroy (lookups);
|
2012-07-27 08:29:32 +02:00
|
|
|
|
|
|
|
hb_shape_plan_destroy (shape_plan);
|
2012-04-24 22:56:37 +02:00
|
|
|
}
|
2019-06-26 22:21:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
#endif
|