src/hb-ot-layout-private.hh | 2 src/hb-ot-shape-complex-arabic.cc | 75 ++++++++++ src/hb-ot-shape-complex-default.cc | 1 src/hb-ot-shape-complex-hangul.cc | 1 src/hb-ot-shape-complex-hebrew.cc | 1 src/hb-ot-shape-complex-indic.cc | 1 src/hb-ot-shape-complex-myanmar.cc | 2 src/hb-ot-shape-complex-private.hh | 12 + src/hb-ot-shape-complex-thai.cc | 1 src/hb-ot-shape-complex-tibetan.cc | 1 src/hb-ot-shape-complex-use.cc | 1 src/hb-ot-shape-normalize.cc | 72 +++++---- src/hb-unicode-private.hh | 6 test/shaping/Makefile.am | 1 test/shaping/fonts/sha1sum/24b8d24d00ae86f49791b746da4c9d3f717a51a8.ttf |binary test/shaping/fonts/sha1sum/94a5d6fb15a27521fba9ea4aee9cb39b2d03322a.ttf |binary test/shaping/record-test.sh | 2 test/shaping/tests/arabic-mark-order.tests | 2 18 files changed, 147 insertions(+), 34 deletions(-)
New commits: commit ab8d70ec7023e51ba6fd7267d2b41c5f95ef0787 Author: Behdad Esfahbod <beh...@behdad.org> Date: Wed Oct 4 14:47:10 2017 +0200 [arabic] Implement Unicode Arabic Mark Ordering Algorithm UTR#53 Fixes https://github.com/behdad/harfbuzz/issues/509 diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc index ed7b3f2d..28dd4e1f 100644 --- a/src/hb-ot-shape-complex-arabic.cc +++ b/src/hb-ot-shape-complex-arabic.cc @@ -613,6 +613,80 @@ postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); } +/* http://www.unicode.org/reports/tr53/tr53-1.pdf */ + +static hb_codepoint_t +modifier_combining_marks[] = +{ + 0x0654u, /* ARABIC HAMZA ABOVE */ + 0x0655u, /* ARABIC HAMZA BELOW */ + 0x0658u, /* ARABIC MARK NOON GHUNNA */ + 0x06DCu, /* ARABIC SMALL HIGH SEEN */ + 0x06E3u, /* ARABIC SMALL LOW SEEN */ + 0x06E7u, /* ARABIC SMALL HIGH YEH */ + 0x06E8u, /* ARABIC SMALL HIGH NOON */ + 0x08F3u, /* ARABIC SMALL HIGH WAW */ +}; + +static inline bool +info_is_mcm (const hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + for (unsigned int i = 0; i < ARRAY_LENGTH (modifier_combining_marks); i++) + if (u == modifier_combining_marks[i]) + return true; + return false; +} + +static void +reorder_marks_arabic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end) +{ + hb_glyph_info_t *info = buffer->info; + + unsigned int i = start; + for (unsigned int cc = 220; cc <= 230; cc += 10) + { + DEBUG_MSG (ARABIC, buffer, "Looking for %d's starting at %d\n", cc, i); + while (i < end && info_cc(info[i]) < cc) + i++; + DEBUG_MSG (ARABIC, buffer, "Looking for %d's stopped at %d\n", cc, i); + + if (i == end) + break; + + if (info_cc(info[i]) > cc) + continue; + + /* Technically we should also check "info_cc(info[j]) == cc" + * in the following loop. But not doing it is safe; we might + * end up moving all the 220 MCMs and 230 MCMs together in one + * move and be done. */ + unsigned int j = i; + while (j < end && info_is_mcm (info[j])) + j++; + DEBUG_MSG (ARABIC, buffer, "Found %d's from %d to %d\n", cc, i, j); + + if (i == j) + continue; + + /* Shift it! */ + DEBUG_MSG (ARABIC, buffer, "Shifting %d's: %d %d\n", cc, i, j); + hb_glyph_info_t temp[HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS]; + assert (j - i <= ARRAY_LENGTH (temp)); + buffer->merge_out_clusters (start, j); + memmove (temp, &info[i], (j - i) * sizeof (hb_glyph_info_t)); + memmove (&info[start + j - i], &info[start], (i - start) * sizeof (hb_glyph_info_t)); + memmove (&info[start], temp, (j - i) * sizeof (hb_glyph_info_t)); + + start += j - i; + + i = j; + } +} + const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = { "arabic", @@ -627,6 +701,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = NULL, /* compose */ setup_masks_arabic, NULL, /* disable_otl */ + reorder_marks_arabic, HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; diff --git a/src/hb-ot-shape-complex-default.cc b/src/hb-ot-shape-complex-default.cc index 42830ab6..857980cf 100644 --- a/src/hb-ot-shape-complex-default.cc +++ b/src/hb-ot-shape-complex-default.cc @@ -41,6 +41,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default = NULL, /* compose */ NULL, /* setup_masks */ NULL, /* disable_otl */ + NULL, /* reorder_marks */ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc index 0e74802e..63850d36 100644 --- a/src/hb-ot-shape-complex-hangul.cc +++ b/src/hb-ot-shape-complex-hangul.cc @@ -426,6 +426,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hangul = NULL, /* compose */ setup_masks_hangul, NULL, /* disable_otl */ + NULL, /* reorder_marks */ HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; diff --git a/src/hb-ot-shape-complex-hebrew.cc b/src/hb-ot-shape-complex-hebrew.cc index 96f24946..b8ddadc1 100644 --- a/src/hb-ot-shape-complex-hebrew.cc +++ b/src/hb-ot-shape-complex-hebrew.cc @@ -181,6 +181,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew = compose_hebrew, NULL, /* setup_masks */ disable_otl_hebrew, + NULL, /* reorder_marks */ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc index 00130e6c..31dc1c0e 100644 --- a/src/hb-ot-shape-complex-indic.cc +++ b/src/hb-ot-shape-complex-indic.cc @@ -1840,6 +1840,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic = compose_indic, setup_masks_indic, NULL, /* disable_otl */ + NULL, /* reorder_marks */ HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc index 676d4948..4081ed03 100644 --- a/src/hb-ot-shape-complex-myanmar.cc +++ b/src/hb-ot-shape-complex-myanmar.cc @@ -524,6 +524,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_old = NULL, /* compose */ NULL, /* setup_masks */ NULL, /* disable_otl */ + NULL, /* reorder_marks */ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; @@ -542,6 +543,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar = NULL, /* compose */ setup_masks_myanmar, NULL, /* disable_otl */ + NULL, /* reorder_marks */ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, false, /* fallback_position */ }; diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh index 8fadd7cd..97920671 100644 --- a/src/hb-ot-shape-complex-private.hh +++ b/src/hb-ot-shape-complex-private.hh @@ -39,6 +39,8 @@ #define complex_var_u8_1() var2.u8[3] +#define HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS 32 + enum hb_ot_shape_zero_width_marks_type_t { HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, @@ -154,6 +156,16 @@ struct hb_ot_complex_shaper_t */ bool (*disable_otl) (const hb_ot_shape_plan_t *plan); + /* reorder_marks() + * Called during shape(). + * Shapers can use to modify ordering of combining marks. + * May be NULL. + */ + void (*reorder_marks) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end); + hb_ot_shape_zero_width_marks_type_t zero_width_marks; bool fallback_position; diff --git a/src/hb-ot-shape-complex-thai.cc b/src/hb-ot-shape-complex-thai.cc index 924247f1..651c47f8 100644 --- a/src/hb-ot-shape-complex-thai.cc +++ b/src/hb-ot-shape-complex-thai.cc @@ -378,6 +378,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai = NULL, /* compose */ NULL, /* setup_masks */ NULL, /* disable_otl */ + NULL, /* reorder_marks */ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, false,/* fallback_position */ }; diff --git a/src/hb-ot-shape-complex-tibetan.cc b/src/hb-ot-shape-complex-tibetan.cc index aadf59f5..a85ac0f4 100644 --- a/src/hb-ot-shape-complex-tibetan.cc +++ b/src/hb-ot-shape-complex-tibetan.cc @@ -58,6 +58,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_tibetan = NULL, /* compose */ NULL, /* setup_masks */ NULL, /* disable_otl */ + NULL, /* reorder_marks */ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc index a5ab0ab2..ac3a2486 100644 --- a/src/hb-ot-shape-complex-use.cc +++ b/src/hb-ot-shape-complex-use.cc @@ -607,6 +607,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use = compose_use, setup_masks_use, NULL, /* disable_otl */ + NULL, /* reorder_marks */ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, false, /* fallback_position */ }; diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index a5144888..fd9e7c2a 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -345,14 +345,18 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0) break; - /* We are going to do a O(n^2). Only do this if the sequence is short. */ - if (end - i > 10) { + /* We are going to do a O(n^2). Only do this if the sequence is short, + * but not too short ;). */ + if (end - i < 2 || end - i > HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS) { i = end; continue; } buffer->sort (i, end, compare_combining_class); + if (plan->shaper->reorder_marks) + plan->shaper->reorder_marks (plan, buffer, i, end); + i = end; } diff --git a/test/shaping/Makefile.am b/test/shaping/Makefile.am index d29800c4..ecb836db 100644 --- a/test/shaping/Makefile.am +++ b/test/shaping/Makefile.am @@ -44,6 +44,7 @@ TESTS = \ tests/arabic-fallback-shaping.tests \ tests/arabic-feature-order.tests \ tests/arabic-like-joining.tests \ + tests/arabic-mark-order.tests \ tests/automatic-fractions.tests \ tests/cluster.tests \ tests/color-fonts.tests \ diff --git a/test/shaping/fonts/sha1sum/24b8d24d00ae86f49791b746da4c9d3f717a51a8.ttf b/test/shaping/fonts/sha1sum/24b8d24d00ae86f49791b746da4c9d3f717a51a8.ttf new file mode 100644 index 00000000..dc290adb Binary files /dev/null and b/test/shaping/fonts/sha1sum/24b8d24d00ae86f49791b746da4c9d3f717a51a8.ttf differ diff --git a/test/shaping/fonts/sha1sum/94a5d6fb15a27521fba9ea4aee9cb39b2d03322a.ttf b/test/shaping/fonts/sha1sum/94a5d6fb15a27521fba9ea4aee9cb39b2d03322a.ttf new file mode 100644 index 00000000..baf544f6 Binary files /dev/null and b/test/shaping/fonts/sha1sum/94a5d6fb15a27521fba9ea4aee9cb39b2d03322a.ttf differ diff --git a/test/shaping/record-test.sh b/test/shaping/record-test.sh index 886d9466..0259c76a 100755 --- a/test/shaping/record-test.sh +++ b/test/shaping/record-test.sh @@ -43,7 +43,7 @@ if test $? != 0; then fi cp "$fontfile" "$dir/font.ttf" -pyftsubset \ +fonttools subset \ --glyph-names \ --no-hinting \ --layout-features='*' \ diff --git a/test/shaping/tests/arabic-mark-order.tests b/test/shaping/tests/arabic-mark-order.tests new file mode 100644 index 00000000..b48c82fd --- /dev/null +++ b/test/shaping/tests/arabic-mark-order.tests @@ -0,0 +1,2 @@ +fonts/sha1sum/94a5d6fb15a27521fba9ea4aee9cb39b2d03322a.ttf::U+064A,U+064E,U+0670,U+0653,U+0640,U+0654,U+064E,U+0627:[afii57415.zz04=7+481|afii57454=4@25,975+0|uni0654=4@-50,50+0|afii57440=4+650|uni0670_uni0653=0@75,400+0|afii57454=0@750,1125+0|afii57450.calt=0+1331] +fonts/sha1sum/24b8d24d00ae86f49791b746da4c9d3f717a51a8.ttf::U+0628,U+0618,U+0619,U+064E,U+064F,U+0654,U+0658,U+0653,U+0654,U+0651,U+0656,U+0651,U+065C,U+0655,U+0650:[uni0653.small=0@266,2508+0|uni0654=0@308,2151+0|uni0655=0@518,-1544+0|uni065C=0@501,-1453+0|uni0656=0@573,-659+0|uni0650=0@500,133+0|uni0619=0@300,1807+0|uni0618=0@357,1674+0|uni0651064E=0@387,1178+0|uni0651=0@402,764+0|uni0658=0@424,404+0|uni0654064F=0@540,-435+0|uni0628=0+1352] commit b6fe0ab636ffac0a246e160b3508cc4841cb1823 Author: Behdad Esfahbod <beh...@behdad.org> Date: Wed Oct 4 13:37:08 2017 +0200 Add info_cc() convenience macro diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh index d4ac60b0..6717adeb 100644 --- a/src/hb-ot-layout-private.hh +++ b/src/hb-ot-layout-private.hh @@ -347,6 +347,8 @@ _hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info) return _hb_glyph_info_is_unicode_mark (info) ? info->unicode_props()>>8 : 0; } +#define info_cc(info) (_hb_glyph_info_get_modified_combining_class (&(info))) + static inline bool _hb_glyph_info_is_unicode_space (const hb_glyph_info_t *info) { diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index 63f1d2d3..a5144888 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -384,7 +384,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, if (/* If there's anything between the starter and this char, they should have CCC * smaller than this character's. */ (starter == buffer->out_len - 1 || - _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) && + info_cc (buffer->prev()) < info_cc (buffer->cur())) && /* And compose. */ c.compose (&c, buffer->out_info[starter].codepoint, @@ -409,14 +409,14 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, else if (/* We sometimes custom-tailor the sorted order of marks. In that case, stop * trying to combine as soon as combining-class drops. */ starter < buffer->out_len - 1 && - _hb_glyph_info_get_modified_combining_class (&buffer->prev()) > _hb_glyph_info_get_modified_combining_class (&buffer->cur())) + info_cc (buffer->prev()) > info_cc (buffer->cur())) combine = false; } /* Blocked, or doesn't compose. */ buffer->next_glyph (); - if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0) + if (info_cc (buffer->prev()) == 0) { starter = buffer->out_len - 1; combine = true; commit 7f9e7f8689e1d260596f5256947dfbd474afb1ec Author: Behdad Esfahbod <beh...@behdad.org> Date: Wed Oct 4 13:20:33 2017 +0200 Adjust normalizer for out-of-order marks We are going to implement Unicode Arabic Mark Ordering Algorithm: http://www.unicode.org/reports/tr53/tr53-1.pdf which will reorder marks out of their sorted ccc order. Adjust normalizer to stop combining as soon as dangerous ordering is detected. diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index 94a3d7d2..63f1d2d3 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -369,46 +369,58 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, buffer->clear_output (); count = buffer->len; unsigned int starter = 0; + bool combine = true; buffer->next_glyph (); while (buffer->idx < count && !buffer->in_error) { hb_codepoint_t composed, glyph; - if (/* We don't try to compose a non-mark character with it's preceding starter. + if (combine && + /* We don't try to compose a non-mark character with it's preceding starter. * This is both an optimization to avoid trying to compose every two neighboring * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */ - HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur())) && - /* If there's anything between the starter and this char, they should have CCC - * smaller than this character's. */ - (starter == buffer->out_len - 1 || - _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) && - /* And compose. */ - c.compose (&c, - buffer->out_info[starter].codepoint, - buffer->cur().codepoint, - &composed) && - /* And the font has glyph for the composite. */ - font->get_nominal_glyph (composed, &glyph)) + HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur()))) { - /* Composes. */ - buffer->next_glyph (); /* Copy to out-buffer. */ - if (unlikely (buffer->in_error)) - return; - buffer->merge_out_clusters (starter, buffer->out_len); - buffer->out_len--; /* Remove the second composable. */ - /* Modify starter and carry on. */ - buffer->out_info[starter].codepoint = composed; - buffer->out_info[starter].glyph_index() = glyph; - _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer); - - continue; + if (/* If there's anything between the starter and this char, they should have CCC + * smaller than this character's. */ + (starter == buffer->out_len - 1 || + _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) && + /* And compose. */ + c.compose (&c, + buffer->out_info[starter].codepoint, + buffer->cur().codepoint, + &composed) && + /* And the font has glyph for the composite. */ + font->get_nominal_glyph (composed, &glyph)) + { + /* Composes. */ + buffer->next_glyph (); /* Copy to out-buffer. */ + if (unlikely (buffer->in_error)) + return; + buffer->merge_out_clusters (starter, buffer->out_len); + buffer->out_len--; /* Remove the second composable. */ + /* Modify starter and carry on. */ + buffer->out_info[starter].codepoint = composed; + buffer->out_info[starter].glyph_index() = glyph; + _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer); + + continue; + } + else if (/* We sometimes custom-tailor the sorted order of marks. In that case, stop + * trying to combine as soon as combining-class drops. */ + starter < buffer->out_len - 1 && + _hb_glyph_info_get_modified_combining_class (&buffer->prev()) > _hb_glyph_info_get_modified_combining_class (&buffer->cur())) + combine = false; } /* Blocked, or doesn't compose. */ buffer->next_glyph (); if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0) + { starter = buffer->out_len - 1; + combine = true; + } } buffer->swap_buffers (); commit a252ad61f077c3b7bbfd8335e1b105a57beb58ce Author: Behdad Esfahbod <beh...@behdad.org> Date: Wed Oct 4 13:07:08 2017 +0200 Minor diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh index eaa12d8f..44a7471c 100644 --- a/src/hb-unicode-private.hh +++ b/src/hb-unicode-private.hh @@ -108,7 +108,7 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE /* XXX This hack belongs to the Myanmar shaper. */ if (unlikely (unicode == 0x1037u)) unicode = 0x103Au; - /* XXX This hack belongs to the SEA shaper (for Tai Tham): + /* XXX This hack belongs to the USE shaper (for Tai Tham): * Reorder SAKOT to ensure it comes after any tone marks. */ if (unlikely (unicode == 0x1A60u)) return 254; commit 4c05a405acc25c4ef0d70a97c0ae59013abca2df Author: Behdad Esfahbod <beh...@behdad.org> Date: Wed Oct 4 13:06:51 2017 +0200 Revert "Treat HAMZA ABOVE similar to SHADD for sorting purposes" This reverts commit 5a330575768f5a213072230b9ec8faabac9c5737. Proper fix coming soon. diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh index cd6c67c8..eaa12d8f 100644 --- a/src/hb-unicode-private.hh +++ b/src/hb-unicode-private.hh @@ -105,10 +105,6 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE inline unsigned int modified_combining_class (hb_codepoint_t unicode) { - /* XXX This hack belongs to the Arabic shaper: - * Put HAMZA ABOVE in the same class as SHADDA. */ - if (unlikely (unicode == 0x0654u)) unicode = 0x0651u; - /* XXX This hack belongs to the Myanmar shaper. */ if (unlikely (unicode == 0x1037u)) unicode = 0x103Au; _______________________________________________ HarfBuzz mailing list HarfBuzz@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/harfbuzz