Merge pull request #3762 from harfbuzz/safe-to-kashida

Prototype glyph flag safe-to-kashida
This commit is contained in:
Behdad Esfahbod 2022-07-30 10:33:27 -06:00 committed by GitHub
commit 7c9e1ffa7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 7 deletions

View File

@ -142,6 +142,11 @@ typedef struct hb_glyph_info_t {
* shaping, otherwise the buffer flag will not be * shaping, otherwise the buffer flag will not be
* reliably produced. * reliably produced.
* Since: 4.0.0 * Since: 4.0.0
* @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.
Since: REPLACEME
* @HB_GLYPH_FLAG_DEFINED: All the currently defined flags. * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags.
* *
* Flags for #hb_glyph_info_t. * Flags for #hb_glyph_info_t.
@ -151,8 +156,9 @@ typedef struct hb_glyph_info_t {
typedef enum { /*< flags >*/ typedef enum { /*< flags >*/
HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001,
HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002, HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002,
HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL = 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_glyph_flags_t;
HB_EXTERN hb_glyph_flags_t HB_EXTERN hb_glyph_flags_t
@ -373,6 +379,10 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer);
* flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT * flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT
* glyph-flag should be produced by the shaper. By default * glyph-flag should be produced by the shaper. By default
* it will not be produced since it incurs a cost. Since: 4.0.0 * it will not be produced since it incurs a cost. Since: 4.0.0
* @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 * @HB_BUFFER_FLAG_DEFINED: All currently defined flags: Since: 4.4.0
* *
* Flags for #hb_buffer_t. * Flags for #hb_buffer_t.
@ -388,8 +398,9 @@ typedef enum { /*< flags >*/
HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u, HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u,
HB_BUFFER_FLAG_VERIFY = 0x00000020u, HB_BUFFER_FLAG_VERIFY = 0x00000020u,
HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT = 0x00000040u, HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT = 0x00000040u,
HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL = 0x00000070u,
HB_BUFFER_FLAG_DEFINED = 0x0000007Fu HB_BUFFER_FLAG_DEFINED = 0x000000FFu
} hb_buffer_flags_t; } hb_buffer_flags_t;
HB_EXTERN void HB_EXTERN void

View File

@ -462,6 +462,17 @@ struct hb_buffer_t
start, end, start, end,
true); true);
} }
void safe_to_insert_tatweel (unsigned int start = 0, unsigned int end = -1)
{
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_TATWEEL,
start, end,
true);
}
void unsafe_to_concat (unsigned int start = 0, unsigned int end = -1) void unsafe_to_concat (unsigned int start = 0, unsigned int end = -1)
{ {
if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0)) if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0))

View File

@ -1141,6 +1141,16 @@ hb_propagate_flags (hb_buffer_t *buffer)
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS)) if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS))
return; return;
/* If we are producing SAFE_TO_INSERT_TATWEEL, then do two things:
*
* - 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_tatweel = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL;
hb_glyph_info_t *info = buffer->info; hb_glyph_info_t *info = buffer->info;
foreach_cluster (buffer, start, end) foreach_cluster (buffer, start, end)
@ -1148,9 +1158,18 @@ hb_propagate_flags (hb_buffer_t *buffer)
unsigned int mask = 0; unsigned int mask = 0;
for (unsigned int i = start; i < end; i++) for (unsigned int i = start; i < end; i++)
mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED; mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED;
if (flip_tatweel)
{
if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
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;
}
if (mask) if (mask)
for (unsigned int i = start; i < end; i++) for (unsigned int i = start; i < end; i++)
info[i].mask |= mask; info[i].mask = mask;
} }
} }

View File

@ -331,7 +331,7 @@ arabic_joining (hb_buffer_t *buffer)
if (entry->prev_action != NONE && prev != UINT_MAX) if (entry->prev_action != NONE && prev != UINT_MAX)
{ {
info[prev].arabic_shaping_action() = entry->prev_action; info[prev].arabic_shaping_action() = entry->prev_action;
buffer->unsafe_to_break (prev, i + 1); buffer->safe_to_insert_tatweel (prev, i + 1);
} }
else else
{ {
@ -365,7 +365,7 @@ arabic_joining (hb_buffer_t *buffer)
if (entry->prev_action != NONE && prev != UINT_MAX) if (entry->prev_action != NONE && prev != UINT_MAX)
{ {
info[prev].arabic_shaping_action() = entry->prev_action; info[prev].arabic_shaping_action() = entry->prev_action;
buffer->unsafe_to_break (prev, buffer->len); buffer->safe_to_insert_tatweel (prev, buffer->len);
} }
else if (2 <= state && state <= 5) /* States that have a possible prev_action. */ else if (2 <= state && state <= 5) /* States that have a possible prev_action. */
{ {

View File

@ -53,6 +53,7 @@ struct shape_options_t
(eot ? HB_BUFFER_FLAG_EOT : 0) | (eot ? HB_BUFFER_FLAG_EOT : 0) |
(verify ? HB_BUFFER_FLAG_VERIFY : 0) | (verify ? HB_BUFFER_FLAG_VERIFY : 0) |
(unsafe_to_concat ? HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT : 0) | (unsafe_to_concat ? HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT : 0) |
(safe_to_insert_tatweel ? HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL : 0) |
(preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) | (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) |
(remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) | (remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) |
0)); 0));
@ -139,6 +140,7 @@ struct shape_options_t
hb_bool_t normalize_glyphs = false; hb_bool_t normalize_glyphs = false;
hb_bool_t verify = false; hb_bool_t verify = false;
hb_bool_t unsafe_to_concat = false; hb_bool_t unsafe_to_concat = false;
hb_bool_t safe_to_insert_tatweel = false;
unsigned int num_iterations = 1; 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"}, {"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}, {"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}, {"unsafe-to-concat",0, 0, G_OPTION_ARG_NONE, &this->unsafe_to_concat, "Produce unsafe-to-concat 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}, {"verify", 0, 0, G_OPTION_ARG_NONE, &this->verify, "Perform sanity checks on shaping results", nullptr},
{"num-iterations", 'n', G_OPTION_FLAG_IN_MAIN, {"num-iterations", 'n', G_OPTION_FLAG_IN_MAIN,
G_OPTION_ARG_INT, &this->num_iterations, "Run shaper N times (default: 1)", "N"}, G_OPTION_ARG_INT, &this->num_iterations, "Run shaper N times (default: 1)", "N"},