diff options
Diffstat (limited to 'src/3rdparty/harfbuzz-ng/src/hb-ot-shape.cc')
-rw-r--r-- | src/3rdparty/harfbuzz-ng/src/hb-ot-shape.cc | 835 |
1 files changed, 518 insertions, 317 deletions
diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-shape.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-shape.cc index 2f28b5620d..5d9a70cda2 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-shape.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-shape.cc @@ -26,60 +26,305 @@ * Google Author(s): Behdad Esfahbod */ -#define HB_SHAPER ot -#define hb_ot_shaper_face_data_t hb_ot_layout_t -#define hb_ot_shaper_shape_plan_data_t hb_ot_shape_plan_t -#include "hb-shaper-impl-private.hh" - -#include "hb-ot-shape-private.hh" -#include "hb-ot-shape-complex-private.hh" -#include "hb-ot-shape-fallback-private.hh" -#include "hb-ot-shape-normalize-private.hh" - -#include "hb-ot-layout-private.hh" -#include "hb-unicode-private.hh" -#include "hb-set-private.hh" - - -static hb_tag_t common_features[] = { - HB_TAG('c','c','m','p'), - HB_TAG('l','o','c','l'), - HB_TAG('m','a','r','k'), - HB_TAG('m','k','m','k'), - HB_TAG('r','l','i','g'), -}; +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#ifdef HB_NO_OT_LAYOUT +#error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT." +#endif + +#include "hb-shaper-impl.hh" + +#include "hb-ot-shape.hh" +#include "hb-ot-shape-complex.hh" +#include "hb-ot-shape-fallback.hh" +#include "hb-ot-shape-normalize.hh" + +#include "hb-ot-face.hh" + +#include "hb-set.hh" +#include "hb-aat-layout.hh" -static hb_tag_t horizontal_features[] = { - HB_TAG('c','a','l','t'), - HB_TAG('c','l','i','g'), - HB_TAG('c','u','r','s'), - HB_TAG('k','e','r','n'), - HB_TAG('l','i','g','a'), - HB_TAG('r','c','l','t'), + +/** + * SECTION:hb-ot-shape + * @title: hb-ot-shape + * @short_description: OpenType shaping support + * @include: hb-ot.h + * + * Support functions for OpenType shaping related queries. + **/ + + +static void +hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, + const hb_feature_t *user_features, + unsigned int num_user_features); + +#ifndef HB_NO_AAT_SHAPE +static inline bool +_hb_apply_morx (hb_face_t *face) +{ + if (hb_options ().aat && + hb_aat_layout_has_substitution (face)) + return true; + + /* Ignore empty GSUB tables. */ + return (!hb_ot_layout_has_substitution (face) || + !hb_ot_layout_table_get_script_tags (face, + HB_OT_TAG_GSUB, + 0, nullptr, nullptr)) && + hb_aat_layout_has_substitution (face); +} +#endif + +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), + aat_map (face, props) +#ifndef HB_NO_AAT_SHAPE + , apply_morx (_hb_apply_morx (face)) +#endif +{ + 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; + + if (apply_morx) + shaper = &_hb_ot_complex_shaper_default; +} + +void +hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, + const hb_ot_shape_plan_key_t &key) +{ + plan.props = props; + plan.shaper = shaper; + map.compile (plan.map, key); +#ifndef HB_NO_AAT_SHAPE + if (apply_morx) + aat_map.compile (plan.aat_map); +#endif + +#ifndef HB_NO_OT_SHAPE_FRACTIONS + 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); +#endif + + plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m')); + hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ? + HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'); +#ifndef HB_NO_OT_KERN + plan.kern_mask = plan.map.get_mask (kern_tag); + plan.requested_kerning = !!plan.kern_mask; +#endif +#ifndef HB_NO_AAT_SHAPE + plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); + plan.requested_tracking = !!plan.trak_mask; +#endif + + bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; + bool disable_gpos = plan.shaper->gpos_tag && + plan.shaper->gpos_tag != plan.map.chosen_script[1]; + + /* + * 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. + */ + +#ifndef HB_NO_AAT_SHAPE + plan.apply_morx = apply_morx; +#endif + + /* + * Decide who does positioning. GPOS, kerx, kern, or fallback. + */ + + if (0) + ; +#ifndef HB_NO_AAT_SHAPE + else if (hb_options ().aat && hb_aat_layout_has_positioning (face)) + plan.apply_kerx = true; +#endif + else if (!apply_morx && !disable_gpos && hb_ot_layout_has_positioning (face)) + plan.apply_gpos = true; +#ifndef HB_NO_AAT_SHAPE + else if (hb_aat_layout_has_positioning (face)) + plan.apply_kerx = true; +#endif + + if (!plan.apply_kerx && !has_gpos_kern) + { + /* Apparently Apple applies kerx if GPOS kern was not applied. */ +#ifndef HB_NO_AAT_SHAPE + if (hb_aat_layout_has_positioning (face)) + plan.apply_kerx = true; + else +#endif +#ifndef HB_NO_OT_KERN + if (hb_ot_layout_has_kerning (face)) + plan.apply_kern = true; +#endif + } + + plan.zero_marks = script_zero_marks && + !plan.apply_kerx && + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_machine_kerning (face) +#endif + ); + plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); + + plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos && + !plan.apply_kerx && + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_cross_kerning (face) +#endif + ); + + plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && + script_fallback_mark_positioning; + +#ifndef HB_NO_AAT_SHAPE + /* Currently we always apply trak. */ + plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face); +#endif +} + +bool +hb_ot_shape_plan_t::init0 (hb_face_t *face, + const hb_shape_plan_key_t *key) +{ + map.init (); +#ifndef HB_NO_AAT_SHAPE + aat_map.init (); +#endif + + hb_ot_shape_planner_t planner (face, + &key->props); + + hb_ot_shape_collect_features (&planner, + key->user_features, + key->num_user_features); + + planner.compile (*this, key->ot); + + if (shaper->data_create) + { + data = shaper->data_create (this); + if (unlikely (!data)) + return false; + } + + return true; +} + +void +hb_ot_shape_plan_t::fini () +{ + if (shaper->data_destroy) + shaper->data_destroy (const_cast<void *> (data)); + + map.fini (); +#ifndef HB_NO_AAT_SHAPE + aat_map.fini (); +#endif +} + +void +hb_ot_shape_plan_t::substitute (hb_font_t *font, + hb_buffer_t *buffer) const +{ +#ifndef HB_NO_AAT_SHAPE + if (unlikely (apply_morx)) + hb_aat_layout_substitute (this, font, buffer); + else +#endif + 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); +#ifndef HB_NO_AAT_SHAPE + else if (this->apply_kerx) + hb_aat_layout_position (this, font, buffer); +#endif +#ifndef HB_NO_OT_KERN + else if (this->apply_kern) + hb_ot_layout_kern (this, font, buffer); +#endif + else + _hb_ot_shape_fallback_kern (this, font, buffer); + +#ifndef HB_NO_AAT_SHAPE + if (this->apply_trak) + hb_aat_layout_track (this, font, buffer); +#endif +} + + +static const hb_ot_map_feature_t +common_features[] = +{ + {HB_TAG('a','b','v','m'), F_GLOBAL}, + {HB_TAG('b','l','w','m'), F_GLOBAL}, + {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}, }; +static const hb_ot_map_feature_t +horizontal_features[] = +{ + {HB_TAG('c','a','l','t'), F_GLOBAL}, + {HB_TAG('c','l','i','g'), F_GLOBAL}, + {HB_TAG('c','u','r','s'), F_GLOBAL}, + {HB_TAG('d','i','s','t'), F_GLOBAL}, + {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK}, + {HB_TAG('l','i','g','a'), F_GLOBAL}, + {HB_TAG('r','c','l','t'), F_GLOBAL}, +}; static void hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, - const hb_segment_properties_t *props, const hb_feature_t *user_features, unsigned int num_user_features) { hb_ot_map_builder_t *map = &planner->map; - map->add_global_bool_feature (HB_TAG('r','v','r','n')); + map->enable_feature (HB_TAG('r','v','r','n')); map->add_gsub_pause (nullptr); - switch (props->direction) { + switch (planner->props.direction) { case HB_DIRECTION_LTR: - map->add_global_bool_feature (HB_TAG ('l','t','r','a')); - map->add_global_bool_feature (HB_TAG ('l','t','r','m')); + map->enable_feature (HB_TAG ('l','t','r','a')); + map->enable_feature (HB_TAG ('l','t','r','m')); break; case HB_DIRECTION_RTL: - map->add_global_bool_feature (HB_TAG ('r','t','l','a')); - map->add_feature (HB_TAG ('r','t','l','m'), 1, F_NONE); + map->enable_feature (HB_TAG ('r','t','l','a')); + map->add_feature (HB_TAG ('r','t','l','m')); break; case HB_DIRECTION_TTB: case HB_DIRECTION_BTT: @@ -88,39 +333,68 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, break; } - map->add_feature (HB_TAG ('f','r','a','c'), 1, F_NONE); - map->add_feature (HB_TAG ('n','u','m','r'), 1, F_NONE); - map->add_feature (HB_TAG ('d','n','o','m'), 1, F_NONE); +#ifndef HB_NO_OT_SHAPE_FRACTIONS + /* Automatic fractions. */ + 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')); +#endif + + /* Random! */ + map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); + +#ifndef HB_NO_AAT_SHAPE + /* Tracking. We enable dummy feature here just to allow disabling + * AAT 'trak' table using features. + * https://github.com/harfbuzz/harfbuzz/issues/1303 */ + map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK); +#endif + + map->enable_feature (HB_TAG ('H','A','R','F')); if (planner->shaper->collect_features) planner->shaper->collect_features (planner); + map->enable_feature (HB_TAG ('B','U','Z','Z')); + for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++) - map->add_global_bool_feature (common_features[i]); + map->add_feature (common_features[i]); - if (HB_DIRECTION_IS_HORIZONTAL (props->direction)) + if (HB_DIRECTION_IS_HORIZONTAL (planner->props.direction)) for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++) - map->add_feature (horizontal_features[i], 1, F_GLOBAL | - (horizontal_features[i] == HB_TAG('k','e','r','n') ? - F_HAS_FALLBACK : F_NONE)); + map->add_feature (horizontal_features[i]); else { /* 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: * https://github.com/harfbuzz/harfbuzz/issues/63 */ - map->add_feature (HB_TAG ('v','e','r','t'), 1, F_GLOBAL | F_GLOBAL_SEARCH); + map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH); } - if (planner->shaper->override_features) - planner->shaper->override_features (planner); - - for (unsigned int i = 0; i < num_user_features; i++) { + for (unsigned int i = 0; i < num_user_features; i++) + { const hb_feature_t *feature = &user_features[i]; - map->add_feature (feature->tag, feature->value, - (feature->start == 0 && feature->end == (unsigned int) -1) ? - F_GLOBAL : F_NONE); + map->add_feature (feature->tag, + (feature->start == HB_FEATURE_GLOBAL_START && + feature->end == HB_FEATURE_GLOBAL_END) ? F_GLOBAL : F_NONE, + feature->value); } + +#ifndef HB_NO_AAT_SHAPE + 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); + } + } +#endif + + if (planner->shaper->override_features) + planner->shaper->override_features (planner); } @@ -128,18 +402,17 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, * shaper face data */ -HB_SHAPER_DATA_ENSURE_DEFINE(ot, face) +struct hb_ot_face_data_t {}; -hb_ot_shaper_face_data_t * +hb_ot_face_data_t * _hb_ot_shaper_face_data_create (hb_face_t *face) { - return _hb_ot_layout_create (face); + return (hb_ot_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void -_hb_ot_shaper_face_data_destroy (hb_ot_shaper_face_data_t *data) +_hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) { - _hb_ot_layout_destroy (data); } @@ -147,64 +420,17 @@ _hb_ot_shaper_face_data_destroy (hb_ot_shaper_face_data_t *data) * shaper font data */ -HB_SHAPER_DATA_ENSURE_DEFINE(ot, font) - -struct hb_ot_shaper_font_data_t {}; +struct hb_ot_font_data_t {}; -hb_ot_shaper_font_data_t * +hb_ot_font_data_t * _hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED) { - return (hb_ot_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; -} - -void -_hb_ot_shaper_font_data_destroy (hb_ot_shaper_font_data_t *data) -{ -} - - -/* - * shaper shape_plan data - */ - -hb_ot_shaper_shape_plan_data_t * -_hb_ot_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan, - const hb_feature_t *user_features, - unsigned int num_user_features, - const int *coords, - unsigned int num_coords) -{ - hb_ot_shape_plan_t *plan = (hb_ot_shape_plan_t *) calloc (1, sizeof (hb_ot_shape_plan_t)); - if (unlikely (!plan)) - return nullptr; - - hb_ot_shape_planner_t planner (shape_plan); - - planner.shaper = hb_ot_shape_complex_categorize (&planner); - - hb_ot_shape_collect_features (&planner, &shape_plan->props, - user_features, num_user_features); - - planner.compile (*plan, coords, num_coords); - - if (plan->shaper->data_create) { - plan->data = plan->shaper->data_create (plan); - if (unlikely (!plan->data)) - return nullptr; - } - - return plan; + return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void -_hb_ot_shaper_shape_plan_data_destroy (hb_ot_shaper_shape_plan_data_t *plan) +_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED) { - if (plan->shaper->data_destroy) - plan->shaper->data_destroy (const_cast<void *> (plan->data)); - - plan->finish (); - - free (plan); } @@ -222,8 +448,6 @@ struct hb_ot_shape_context_t unsigned int num_user_features; /* Transient stuff */ - bool fallback_positioning; - bool fallback_glyph_classes; hb_direction_t target_direction; }; @@ -237,19 +461,66 @@ struct hb_ot_shape_context_t static void hb_set_unicode_props (hb_buffer_t *buffer) { + /* 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 + */ unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) + { _hb_glyph_info_set_unicode_props (&info[i], buffer); + + /* Marks are already set as continuation by the above line. + * 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))) + { + _hb_glyph_info_set_continuation (&info[i]); + } +#ifndef HB_NO_EMOJI_SEQUENCES + else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) + { + _hb_glyph_info_set_continuation (&info[i]); + if (i + 1 < count && + _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint)) + { + i++; + _hb_glyph_info_set_unicode_props (&info[i], buffer); + _hb_glyph_info_set_continuation (&info[i]); + } + } +#endif + /* 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 + * + * 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. + * 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]); + } } static void hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || buffer->context_len[0] || - _hb_glyph_info_get_general_category (&buffer->info[0]) != - HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) + !_hb_glyph_info_is_unicode_mark (&buffer->info[0])) return; if (!font->has_glyph (0x25CCu)) @@ -266,9 +537,8 @@ hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) info.cluster = buffer->cur().cluster; info.mask = buffer->cur().mask; buffer->output_info (info); - while (buffer->idx < buffer->len && !buffer->in_error) + while (buffer->idx < buffer->len && buffer->successful) buffer->next_glyph (); - buffer->swap_buffers (); } @@ -278,59 +548,40 @@ hb_form_clusters (hb_buffer_t *buffer) if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) return; - /* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */ - unsigned int base = 0; - unsigned int count = buffer->len; - hb_glyph_info_t *info = buffer->info; - for (unsigned int i = 1; i < count; i++) - { - if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])) && - !_hb_glyph_info_is_joiner (&info[i]))) - { - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) - buffer->merge_clusters (base, i); - else - buffer->unsafe_to_break (base, i); - base = i; - } - } if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) - buffer->merge_clusters (base, count); + foreach_grapheme (buffer, start, end) + buffer->merge_clusters (start, end); else - buffer->unsafe_to_break (base, count); + foreach_grapheme (buffer, start, end) + buffer->unsafe_to_break (start, end); } static void hb_ensure_native_direction (hb_buffer_t *buffer) { hb_direction_t direction = buffer->props.direction; + hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script); /* 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. */ - if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) || - (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB)) + if ((HB_DIRECTION_IS_HORIZONTAL (direction) && + direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) || + (HB_DIRECTION_IS_VERTICAL (direction) && + direction != HB_DIRECTION_TTB)) { - /* Same loop as hb_form_clusters(). - * Since form_clusters() merged clusters already, we don't merge. */ - unsigned int base = 0; - unsigned int count = buffer->len; - hb_glyph_info_t *info = buffer->info; - for (unsigned int i = 1; i < count; i++) - { - if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) - { - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) - buffer->merge_clusters (base, i); - buffer->reverse_range (base, i); - base = i; - } - } if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) - buffer->merge_clusters (base, count); - buffer->reverse_range (base, count); + 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); buffer->reverse (); @@ -339,10 +590,12 @@ hb_ensure_native_direction (hb_buffer_t *buffer) } -/* Substitute */ +/* + * Substitute + */ static inline void -hb_ot_mirror_chars (hb_ot_shape_context_t *c) +hb_ot_mirror_chars (const hb_ot_shape_context_t *c) { if (HB_DIRECTION_IS_FORWARD (c->target_direction)) return; @@ -363,8 +616,12 @@ hb_ot_mirror_chars (hb_ot_shape_context_t *c) } static inline void -hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c) +hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) { +#ifdef HB_NO_OT_SHAPE_FRACTIONS + return; +#endif + if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || !c->plan->has_frac) return; @@ -393,19 +650,19 @@ hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c) while (start && _hb_glyph_info_get_general_category (&info[start - 1]) == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) - start--; + start--; while (end < count && _hb_glyph_info_get_general_category (&info[end]) == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) - end++; + end++; buffer->unsafe_to_break (start, end); for (unsigned int j = start; j < i; j++) - info[j].mask |= pre_mask; + info[j].mask |= pre_mask; info[i].mask |= c->plan->frac_mask; for (unsigned int j = i + 1; j < end; j++) - info[j].mask |= post_mask; + info[j].mask |= post_mask; i = end - 1; } @@ -413,7 +670,7 @@ hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c) } static inline void -hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c) +hb_ot_shape_initialize_masks (const hb_ot_shape_context_t *c) { hb_ot_map_t *map = &c->plan->map; hb_buffer_t *buffer = c->buffer; @@ -423,7 +680,7 @@ hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c) } static inline void -hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) +hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c) { hb_ot_map_t *map = &c->plan->map; hb_buffer_t *buffer = c->buffer; @@ -445,12 +702,11 @@ hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) } static void -hb_ot_zero_width_default_ignorables (hb_ot_shape_context_t *c) +hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer) { - hb_buffer_t *buffer = c->buffer; - if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || - (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) + (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) || + (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES)) return; unsigned int count = buffer->len; @@ -463,82 +719,29 @@ hb_ot_zero_width_default_ignorables (hb_ot_shape_context_t *c) } static void -hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c) +hb_ot_hide_default_ignorables (hb_buffer_t *buffer, + hb_font_t *font) { - hb_buffer_t *buffer = c->buffer; - if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) 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]))) - break; - } - /* No default-ignorables found; return. */ - if (i == count) - return; - - hb_codepoint_t space; - if (c->font->get_nominal_glyph (' ', &space)) + hb_codepoint_t invisible = buffer->invisible; + if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) && + (invisible || font->get_nominal_glyph (' ', &invisible))) { - /* Replace default-ignorables with a zero-advance space glyph. */ - for (/*continue*/; i < count; i++) + /* Replace default-ignorables with a zero-advance invisible glyph. */ + for (unsigned int i = 0; i < count; i++) { if (_hb_glyph_info_is_default_ignorable (&info[i])) - info[i].codepoint = space; + info[i].codepoint = invisible; } } else - { - /* Merge clusters and delete default-ignorables. - * NOTE! We can't use out-buffer as we have positioning data. */ - unsigned int j = i; - for (; i < count; i++) - { - if (_hb_glyph_info_is_default_ignorable (&info[i])) - { - /* Merge clusters. - * Same logic as buffer->delete_glyph(), but for in-place removal. */ - - unsigned int cluster = info[i].cluster; - if (i + 1 < count && cluster == info[i + 1].cluster) - continue; /* Cluster survives; do nothing. */ - - if (j) - { - /* Merge cluster backward. */ - if (cluster < info[j - 1].cluster) - { - unsigned int mask = info[i].mask; - unsigned int old_cluster = info[j - 1].cluster; - for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--) - buffer->set_cluster (info[k - 1], cluster, mask); - } - continue; - } - - if (i + 1 < count) - buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */ - - continue; - } - - if (j != i) - { - info[j] = info[i]; - pos[j] = pos[i]; - } - j++; - } - buffer->len = j; - } + hb_ot_layout_delete_glyphs_inplace (buffer, _hb_glyph_info_is_default_ignorable); } @@ -555,10 +758,10 @@ hb_ot_map_glyphs_fast (hb_buffer_t *buffer) } static inline void -hb_synthesize_glyph_classes (hb_ot_shape_context_t *c) +hb_synthesize_glyph_classes (hb_buffer_t *buffer) { - unsigned int count = c->buffer->len; - hb_glyph_info_t *info = c->buffer->info; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) { hb_ot_layout_glyph_props_flags_t klass; @@ -581,7 +784,7 @@ hb_synthesize_glyph_classes (hb_ot_shape_context_t *c) } static inline void -hb_ot_substitute_default (hb_ot_shape_context_t *c) +hb_ot_substitute_default (const hb_ot_shape_context_t *c) { hb_buffer_t *buffer = c->buffer; @@ -594,8 +797,8 @@ hb_ot_substitute_default (hb_ot_shape_context_t *c) hb_ot_shape_setup_masks (c); /* This is unfortunate to go here, but necessary... */ - if (c->fallback_positioning) - _hb_ot_shape_fallback_position_recategorize_marks (c->plan, c->font, buffer); + if (c->plan->fallback_mark_positioning) + _hb_ot_shape_fallback_mark_position_recategorize_marks (c->plan, c->font, buffer); hb_ot_map_glyphs_fast (buffer); @@ -603,22 +806,20 @@ hb_ot_substitute_default (hb_ot_shape_context_t *c) } static inline void -hb_ot_substitute_complex (hb_ot_shape_context_t *c) +hb_ot_substitute_complex (const hb_ot_shape_context_t *c) { hb_buffer_t *buffer = c->buffer; hb_ot_layout_substitute_start (c->font, buffer); - if (!hb_ot_layout_has_glyph_classes (c->face)) - hb_synthesize_glyph_classes (c); + if (c->plan->fallback_glyph_classes) + hb_synthesize_glyph_classes (c->buffer); c->plan->substitute (c->font, buffer); - - return; } static inline void -hb_ot_substitute (hb_ot_shape_context_t *c) +hb_ot_substitute_pre (const hb_ot_shape_context_t *c) { hb_ot_substitute_default (c); @@ -627,7 +828,23 @@ hb_ot_substitute (hb_ot_shape_context_t *c) hb_ot_substitute_complex (c); } -/* Position */ +static inline void +hb_ot_substitute_post (const hb_ot_shape_context_t *c) +{ + hb_ot_hide_default_ignorables (c->buffer, c->font); +#ifndef HB_NO_AAT_SHAPE + if (c->plan->apply_morx) + hb_aat_layout_remove_deleted_glyphs (c->buffer); +#endif + + if (c->plan->shaper->postprocess_glyphs) + c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); +} + + +/* + * Position + */ static inline void adjust_mark_offsets (hb_glyph_position_t *pos) @@ -652,13 +869,13 @@ zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets) if (_hb_glyph_info_is_mark (&info[i])) { if (adjust_offsets) - adjust_mark_offsets (&buffer->pos[i]); + adjust_mark_offsets (&buffer->pos[i]); zero_mark_width (&buffer->pos[i]); } } static inline void -hb_ot_position_default (hb_ot_shape_context_t *c) +hb_ot_position_default (const hb_ot_shape_context_t *c) { hb_direction_t direction = c->buffer->props.direction; unsigned int count = c->buffer->len; @@ -667,8 +884,8 @@ hb_ot_position_default (hb_ot_shape_context_t *c) if (HB_DIRECTION_IS_HORIZONTAL (direction)) { - for (unsigned int i = 0; i < count; i++) - pos[i].x_advance = c->font->get_glyph_h_advance (info[i].codepoint); + c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]), + &pos[0].x_advance, sizeof(pos[0])); /* 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++) @@ -678,9 +895,10 @@ hb_ot_position_default (hb_ot_shape_context_t *c) } else { + c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]), + &pos[0].y_advance, sizeof(pos[0])); for (unsigned int i = 0; i < count; i++) { - pos[i].y_advance = c->font->get_glyph_v_advance (info[i].codepoint); c->font->subtract_glyph_v_origin (info[i].codepoint, &pos[i].x_offset, &pos[i].y_offset); @@ -691,23 +909,22 @@ hb_ot_position_default (hb_ot_shape_context_t *c) } static inline void -hb_ot_position_complex (hb_ot_shape_context_t *c) +hb_ot_position_complex (const hb_ot_shape_context_t *c) { unsigned int count = c->buffer->len; hb_glyph_info_t *info = c->buffer->info; hb_glyph_position_t *pos = c->buffer->pos; - /* If the font has no GPOS, AND, no fallback positioning will - * happen, 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 + /* 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 * direction is backward we don't shift and it will end up * hanging over the next glyph after the final reordering. - * If fallback positinoing happens or GPOS is present, we don't - * care. + * + * Note: If fallback positinoing happens, we don't care about + * this as it will be overriden. */ - bool adjust_offsets_when_zeroing = c->fallback_positioning && - !c->plan->shaper->fallback_position && + bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing && HB_DIRECTION_IS_FORWARD (c->buffer->props.direction); /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ @@ -721,36 +938,41 @@ hb_ot_position_complex (hb_ot_shape_context_t *c) hb_ot_layout_position_start (c->font, c->buffer); - 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; - } + if (c->plan->zero_marks) + 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; + } - if (likely (!c->fallback_positioning)) - c->plan->position (c->font, c->buffer); + c->plan->position (c->font, c->buffer); - 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; - } + if (c->plan->zero_marks) + 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; + } - /* Finishing off GPOS has to follow a certain order. */ + /* Finish off. Has to follow a certain order. */ hb_ot_layout_position_finish_advances (c->font, c->buffer); - hb_ot_zero_width_default_ignorables (c); + hb_ot_zero_width_default_ignorables (c->buffer); +#ifndef HB_NO_AAT_SHAPE + if (c->plan->apply_morx) + hb_aat_layout_zero_width_deleted_glyphs (c->buffer); +#endif hb_ot_layout_position_finish_offsets (c->font, c->buffer); /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ @@ -759,10 +981,14 @@ hb_ot_position_complex (hb_ot_shape_context_t *c) c->font->subtract_glyph_h_origin (info[i].codepoint, &pos[i].x_offset, &pos[i].y_offset); + + if (c->plan->fallback_mark_positioning) + _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer, + adjust_offsets_when_zeroing); } static inline void -hb_ot_position (hb_ot_shape_context_t *c) +hb_ot_position (const hb_ot_shape_context_t *c) { c->buffer->clear_positions (); @@ -770,17 +996,9 @@ hb_ot_position (hb_ot_shape_context_t *c) hb_ot_position_complex (c); - if (c->fallback_positioning && c->plan->shaper->fallback_position) - _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer); - if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) hb_buffer_reverse (c->buffer); - /* Visual fallback goes here. */ - - if (c->fallback_positioning) - _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer); - _hb_buffer_deallocate_gsubgpos_vars (c->buffer); } @@ -817,22 +1035,17 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c) { c->buffer->deallocate_var_all (); c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; - if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR))) + if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR))) { - c->buffer->max_len = MAX (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR, + c->buffer->max_len = hb_max (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR, (unsigned) HB_BUFFER_MAX_LEN_MIN); } - if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR))) + if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR))) { - c->buffer->max_ops = MAX (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR, + c->buffer->max_ops = hb_max (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR, (unsigned) HB_BUFFER_MAX_OPS_MIN); } - bool disable_otl = c->plan->shaper->disable_otl && c->plan->shaper->disable_otl (c->plan); - //c->fallback_substitute = disable_otl || !hb_ot_layout_has_substitution (c->face); - c->fallback_positioning = disable_otl || !hb_ot_layout_has_positioning (c->face); - c->fallback_glyph_classes = disable_otl || !hb_ot_layout_has_glyph_classes (c->face); - /* Save the original direction, we use it later. */ c->target_direction = c->buffer->props.direction; @@ -851,13 +1064,9 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c) if (c->plan->shaper->preprocess_text) c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); - hb_ot_substitute (c); + hb_ot_substitute_pre (c); hb_ot_position (c); - - hb_ot_hide_default_ignorables (c); - - if (c->plan->shaper->postprocess_glyphs) - c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); + hb_ot_substitute_post (c); hb_propagate_flags (c->buffer); @@ -878,7 +1087,7 @@ _hb_ot_shape (hb_shape_plan_t *shape_plan, const hb_feature_t *features, unsigned int num_features) { - hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features}; + hb_ot_shape_context_t c = {&shape_plan->ot, font, font->face, buffer, features, num_features}; hb_ot_shape_internal (&c); return true; @@ -895,8 +1104,7 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, hb_tag_t table_tag, hb_set_t *lookup_indexes /* OUT */) { - /* XXX Does the first part always succeed? */ - HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookup_indexes); + shape_plan->ot.collect_lookups (table_tag, lookup_indexes); } @@ -932,8 +1140,6 @@ hb_ot_shape_glyphs_closure (hb_font_t *font, unsigned int num_features, hb_set_t *glyphs) { - hb_ot_shape_plan_t plan; - const char *shapers[] = {"ot", nullptr}; hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shapers); @@ -947,17 +1153,12 @@ hb_ot_shape_glyphs_closure (hb_font_t *font, hb_set_t *lookups = hb_set_create (); hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups); - - /* And find transitive closure. */ - hb_set_t *copy = hb_set_create (); - do { - copy->set (glyphs); - for (hb_codepoint_t lookup_index = -1; hb_set_next (lookups, &lookup_index);) - hb_ot_layout_lookup_substitute_closure (font->face, lookup_index, glyphs); - } while (!copy->is_equal (glyphs)); - hb_set_destroy (copy); + hb_ot_layout_lookups_substitute_closure (font->face, lookups, glyphs); hb_set_destroy (lookups); hb_shape_plan_destroy (shape_plan); } + + +#endif |