From 915e12ccbbeaba0ee3777feb26687ce4728a7e89 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 29 Jul 2022 13:55:39 -0600 Subject: [PATCH 1/5] Prototype glyph flag safe-to-kashida Fixes https://github.com/harfbuzz/harfbuzz/issues/3721 --- src/hb-buffer.h | 7 ++++--- src/hb-buffer.hh | 6 ++++++ src/hb-ot-shape.cc | 8 +++++++- src/hb-ot-shaper-arabic.cc | 4 ++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/hb-buffer.h b/src/hb-buffer.h index 632f6b523..820a90517 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -149,10 +149,11 @@ typedef struct hb_glyph_info_t { * Since: 1.5.0 */ typedef enum { /*< flags >*/ - HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, - HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002, + HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002, + HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA = 0x00000004, - HB_GLYPH_FLAG_DEFINED = 0x00000003 /* OR of all defined flags */ + HB_GLYPH_FLAG_DEFINED = 0x00000007 /* OR of all defined flags */ } hb_glyph_flags_t; HB_EXTERN hb_glyph_flags_t diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh index a05f1662e..561c07c43 100644 --- a/src/hb-buffer.hh +++ b/src/hb-buffer.hh @@ -462,6 +462,12 @@ struct hb_buffer_t start, end, true); } + void safe_to_insert_kashida (unsigned int start = 0, unsigned int end = -1) + { + _set_glyph_flags (HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA, + start, end, + true); + } void unsafe_to_concat (unsigned int start = 0, unsigned int end = -1) { if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0)) diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index 2ada84b8a..842c74000 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -1148,9 +1148,15 @@ hb_propagate_flags (hb_buffer_t *buffer) unsigned int mask = 0; for (unsigned int i = start; i < end; i++) mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED; + + if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) + mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA; + if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA) + mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + if (mask) for (unsigned int i = start; i < end; i++) - info[i].mask |= mask; + info[i].mask = mask; } } diff --git a/src/hb-ot-shaper-arabic.cc b/src/hb-ot-shaper-arabic.cc index b331a048b..9e75a740b 100644 --- a/src/hb-ot-shaper-arabic.cc +++ b/src/hb-ot-shaper-arabic.cc @@ -331,7 +331,7 @@ arabic_joining (hb_buffer_t *buffer) if (entry->prev_action != NONE && prev != UINT_MAX) { info[prev].arabic_shaping_action() = entry->prev_action; - buffer->unsafe_to_break (prev, i + 1); + buffer->safe_to_insert_kashida (prev, i + 1); } else { @@ -365,7 +365,7 @@ arabic_joining (hb_buffer_t *buffer) if (entry->prev_action != NONE && prev != UINT_MAX) { info[prev].arabic_shaping_action() = entry->prev_action; - buffer->unsafe_to_break (prev, buffer->len); + buffer->safe_to_insert_kashida (prev, buffer->len); } else if (2 <= state && state <= 5) /* States that have a possible prev_action. */ { From d277addb2f198e7bb710a79d7ae0c97c85613719 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 30 Jul 2022 10:10:21 -0600 Subject: [PATCH 2/5] [buffer] Add HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA --- src/hb-buffer.h | 3 ++- src/hb-buffer.hh | 5 +++++ src/hb-ot-shape.cc | 21 +++++++++++++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/hb-buffer.h b/src/hb-buffer.h index 820a90517..aee2d7392 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -389,8 +389,9 @@ typedef enum { /*< flags >*/ HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u, HB_BUFFER_FLAG_VERIFY = 0x00000020u, HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT = 0x00000040u, + HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA = 0x00000070u, - HB_BUFFER_FLAG_DEFINED = 0x0000007Fu + HB_BUFFER_FLAG_DEFINED = 0x000000FFu } hb_buffer_flags_t; HB_EXTERN void diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh index 561c07c43..c370a3148 100644 --- a/src/hb-buffer.hh +++ b/src/hb-buffer.hh @@ -464,6 +464,11 @@ struct hb_buffer_t } void safe_to_insert_kashida (unsigned int start = 0, unsigned int end = -1) { + if ((flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA) == 0) + { + unsafe_to_break (start, end); + return; + } _set_glyph_flags (HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA, start, end, true); diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index 842c74000..09f21a69b 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -1141,6 +1141,16 @@ hb_propagate_flags (hb_buffer_t *buffer) if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS)) return; + /* If we are producing SAFE_TO_INSERT_KASHIDA, then do two things: + * + * - If the places that the Arabic shaper marked as SAFE_TO_INSERT_KASHIDA, + * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_KASHIDA, + * - Any place that is SAFE_TO_INSERT_KASHIDA, is also now UNSAFE_TO_BREAK. + * + * We couldn't make this interaction earlier. It has to be done here. + */ + bool flip_kashida = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA; + hb_glyph_info_t *info = buffer->info; foreach_cluster (buffer, start, end) @@ -1149,10 +1159,13 @@ hb_propagate_flags (hb_buffer_t *buffer) for (unsigned int i = start; i < end; i++) mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED; - if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) - mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA; - if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA) - mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + if (flip_kashida) + { + if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) + mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA; + if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA) + mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + } if (mask) for (unsigned int i = start; i < end; i++) From 4f09ea5e6bd89add30c8206933efd04b3bbbf1cf Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 30 Jul 2022 10:12:24 -0600 Subject: [PATCH 3/5] [hb-shape] Add --safe-to-insert-kashida flag https://github.com/harfbuzz/harfbuzz/pull/3762 --- util/shape-options.hh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/shape-options.hh b/util/shape-options.hh index 0f41c0a13..e393e81ee 100644 --- a/util/shape-options.hh +++ b/util/shape-options.hh @@ -53,6 +53,7 @@ struct shape_options_t (eot ? HB_BUFFER_FLAG_EOT : 0) | (verify ? HB_BUFFER_FLAG_VERIFY : 0) | (unsafe_to_concat ? HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT : 0) | + (safe_to_insert_kashida ? HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA : 0) | (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) | (remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) | 0)); @@ -139,6 +140,7 @@ struct shape_options_t hb_bool_t normalize_glyphs = false; hb_bool_t verify = false; hb_bool_t unsafe_to_concat = false; + hb_bool_t safe_to_insert_kashida = false; unsigned int num_iterations = 1; }; @@ -263,6 +265,7 @@ shape_options_t::add_options (option_parser_t *parser) {"cluster-level", 0, 0, G_OPTION_ARG_INT, &this->cluster_level, "Cluster merging level (default: 0)", "0/1/2"}, {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE, &this->normalize_glyphs, "Rearrange glyph clusters in nominal order", nullptr}, {"unsafe-to-concat",0, 0, G_OPTION_ARG_NONE, &this->unsafe_to_concat, "Produce unsafe-to-concat glyph flag", nullptr}, + {"safe-to-insert-kashida",0, 0, G_OPTION_ARG_NONE, &this->safe_to_insert_kashida, "Produce safe-to-insert-kashida glyph flag", nullptr}, {"verify", 0, 0, G_OPTION_ARG_NONE, &this->verify, "Perform sanity checks on shaping results", nullptr}, {"num-iterations", 'n', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &this->num_iterations, "Run shaper N times (default: 1)", "N"}, From 73c3ac917247be04d824d7adf3e6c233e92bb063 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 30 Jul 2022 10:21:34 -0600 Subject: [PATCH 4/5] Document kashida stuff --- src/hb-buffer.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/hb-buffer.h b/src/hb-buffer.h index aee2d7392..1e6d1998b 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -142,6 +142,11 @@ typedef struct hb_glyph_info_t { * shaping, otherwise the buffer flag will not be * reliably produced. * Since: 4.0.0 + * @HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA: In scripts that use elongation (Arabic, + Mongolian, Syriac, etc.), this flag signifies + that it is safe to insert a U+0640 TATWEEL + character *before* this cluster for elongation. + Since: REPLACEME * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags. * * Flags for #hb_glyph_info_t. @@ -374,6 +379,10 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT * glyph-flag should be produced by the shaper. By default * it will not be produced since it incurs a cost. Since: 4.0.0 + * @HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA: + * flag indicating that the @HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA + * glyph-flag should be produced by the shaper. By default + * it will not be produced. Since: REPLACEME * @HB_BUFFER_FLAG_DEFINED: All currently defined flags: Since: 4.4.0 * * Flags for #hb_buffer_t. From f43dadb8eea532cb90318a7ffcdd4bf968d3f711 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 30 Jul 2022 10:23:03 -0600 Subject: [PATCH 5/5] Rename kashida -> tatweel --- src/hb-buffer.h | 10 +++++----- src/hb-buffer.hh | 6 +++--- src/hb-ot-shape.cc | 16 ++++++++-------- src/hb-ot-shaper-arabic.cc | 4 ++-- util/shape-options.hh | 6 +++--- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/hb-buffer.h b/src/hb-buffer.h index 1e6d1998b..e04cda3f1 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -142,7 +142,7 @@ typedef struct hb_glyph_info_t { * shaping, otherwise the buffer flag will not be * reliably produced. * Since: 4.0.0 - * @HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA: In scripts that use elongation (Arabic, + * @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL: In scripts that use elongation (Arabic, Mongolian, Syriac, etc.), this flag signifies that it is safe to insert a U+0640 TATWEEL character *before* this cluster for elongation. @@ -156,7 +156,7 @@ typedef struct hb_glyph_info_t { typedef enum { /*< flags >*/ HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002, - HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA = 0x00000004, + HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL = 0x00000004, HB_GLYPH_FLAG_DEFINED = 0x00000007 /* OR of all defined flags */ } hb_glyph_flags_t; @@ -379,8 +379,8 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT * glyph-flag should be produced by the shaper. By default * it will not be produced since it incurs a cost. Since: 4.0.0 - * @HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA: - * flag indicating that the @HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA + * @HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL: + * flag indicating that the @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL * glyph-flag should be produced by the shaper. By default * it will not be produced. Since: REPLACEME * @HB_BUFFER_FLAG_DEFINED: All currently defined flags: Since: 4.4.0 @@ -398,7 +398,7 @@ typedef enum { /*< flags >*/ HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u, HB_BUFFER_FLAG_VERIFY = 0x00000020u, HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT = 0x00000040u, - HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA = 0x00000070u, + HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL = 0x00000070u, HB_BUFFER_FLAG_DEFINED = 0x000000FFu } hb_buffer_flags_t; diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh index c370a3148..26c3f0fac 100644 --- a/src/hb-buffer.hh +++ b/src/hb-buffer.hh @@ -462,14 +462,14 @@ struct hb_buffer_t start, end, true); } - void safe_to_insert_kashida (unsigned int start = 0, unsigned int end = -1) + void safe_to_insert_tatweel (unsigned int start = 0, unsigned int end = -1) { - if ((flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA) == 0) + if ((flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL) == 0) { unsafe_to_break (start, end); return; } - _set_glyph_flags (HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA, + _set_glyph_flags (HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL, start, end, true); } diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index 09f21a69b..3da317a5c 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -1141,15 +1141,15 @@ hb_propagate_flags (hb_buffer_t *buffer) if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS)) return; - /* If we are producing SAFE_TO_INSERT_KASHIDA, then do two things: + /* If we are producing SAFE_TO_INSERT_TATWEEL, then do two things: * - * - If the places that the Arabic shaper marked as SAFE_TO_INSERT_KASHIDA, - * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_KASHIDA, - * - Any place that is SAFE_TO_INSERT_KASHIDA, is also now UNSAFE_TO_BREAK. + * - If the places that the Arabic shaper marked as SAFE_TO_INSERT_TATWEEL, + * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_TATWEEL, + * - Any place that is SAFE_TO_INSERT_TATWEEL, is also now UNSAFE_TO_BREAK. * * We couldn't make this interaction earlier. It has to be done here. */ - bool flip_kashida = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA; + bool flip_tatweel = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL; hb_glyph_info_t *info = buffer->info; @@ -1159,11 +1159,11 @@ hb_propagate_flags (hb_buffer_t *buffer) for (unsigned int i = start; i < end; i++) mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED; - if (flip_kashida) + if (flip_tatweel) { if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) - mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA; - if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_KASHIDA) + mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; + if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; } diff --git a/src/hb-ot-shaper-arabic.cc b/src/hb-ot-shaper-arabic.cc index 9e75a740b..ed2748b82 100644 --- a/src/hb-ot-shaper-arabic.cc +++ b/src/hb-ot-shaper-arabic.cc @@ -331,7 +331,7 @@ arabic_joining (hb_buffer_t *buffer) if (entry->prev_action != NONE && prev != UINT_MAX) { info[prev].arabic_shaping_action() = entry->prev_action; - buffer->safe_to_insert_kashida (prev, i + 1); + buffer->safe_to_insert_tatweel (prev, i + 1); } else { @@ -365,7 +365,7 @@ arabic_joining (hb_buffer_t *buffer) if (entry->prev_action != NONE && prev != UINT_MAX) { info[prev].arabic_shaping_action() = entry->prev_action; - buffer->safe_to_insert_kashida (prev, buffer->len); + buffer->safe_to_insert_tatweel (prev, buffer->len); } else if (2 <= state && state <= 5) /* States that have a possible prev_action. */ { diff --git a/util/shape-options.hh b/util/shape-options.hh index e393e81ee..e24f4f8e0 100644 --- a/util/shape-options.hh +++ b/util/shape-options.hh @@ -53,7 +53,7 @@ struct shape_options_t (eot ? HB_BUFFER_FLAG_EOT : 0) | (verify ? HB_BUFFER_FLAG_VERIFY : 0) | (unsafe_to_concat ? HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT : 0) | - (safe_to_insert_kashida ? HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_KASHIDA : 0) | + (safe_to_insert_tatweel ? HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL : 0) | (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) | (remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) | 0)); @@ -140,7 +140,7 @@ struct shape_options_t hb_bool_t normalize_glyphs = false; hb_bool_t verify = false; hb_bool_t unsafe_to_concat = false; - hb_bool_t safe_to_insert_kashida = false; + hb_bool_t safe_to_insert_tatweel = false; unsigned int num_iterations = 1; }; @@ -265,7 +265,7 @@ shape_options_t::add_options (option_parser_t *parser) {"cluster-level", 0, 0, G_OPTION_ARG_INT, &this->cluster_level, "Cluster merging level (default: 0)", "0/1/2"}, {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE, &this->normalize_glyphs, "Rearrange glyph clusters in nominal order", nullptr}, {"unsafe-to-concat",0, 0, G_OPTION_ARG_NONE, &this->unsafe_to_concat, "Produce unsafe-to-concat glyph flag", nullptr}, - {"safe-to-insert-kashida",0, 0, G_OPTION_ARG_NONE, &this->safe_to_insert_kashida, "Produce safe-to-insert-kashida glyph flag", nullptr}, + {"safe-to-insert-tatweel",0, 0, G_OPTION_ARG_NONE, &this->safe_to_insert_tatweel, "Produce safe-to-insert-tatweel glyph flag", nullptr}, {"verify", 0, 0, G_OPTION_ARG_NONE, &this->verify, "Perform sanity checks on shaping results", nullptr}, {"num-iterations", 'n', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &this->num_iterations, "Run shaper N times (default: 1)", "N"},