diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index 1f526e301..9472af4f1 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -560,7 +560,8 @@ HB_OT_TAG_DEFAULT_LANGUAGE HB_OT_TAG_DEFAULT_SCRIPT hb_ot_tag_to_language hb_ot_tag_to_script -hb_ot_tags +hb_ot_tags_from_script_and_language +hb_ot_tags_to_script_and_language
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc index 413ec48dc..45d7dbdcf 100644 --- a/src/hb-ot-map.cc +++ b/src/hb-ot-map.cc @@ -59,7 +59,7 @@ hb_ot_map_builder_t::hb_ot_map_builder_t (hb_face_t *face_, hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; - hb_ot_tags (props.script, props.language, &script_count, script_tags, &language_count, language_tags); + hb_ot_tags_from_script_and_language (props.script, props.language, &script_count, script_tags, &language_count, language_tags); for (unsigned int table_index = 0; table_index < 2; table_index++) { hb_tag_t table_tag = table_tags[table_index]; diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc index 0d4c06030..0987fe131 100644 --- a/src/hb-ot-tag.cc +++ b/src/hb-ot-tag.cc @@ -121,7 +121,7 @@ hb_ot_tags_from_script (hb_script_t script, { unsigned int count = 2; hb_tag_t tags[2]; - hb_ot_tags (script, HB_LANGUAGE_INVALID, &count, tags, nullptr, nullptr); + hb_ot_tags_from_script_and_language (script, HB_LANGUAGE_INVALID, &count, tags, nullptr, nullptr); *script_tag_1 = count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_SCRIPT; *script_tag_2 = count > 1 ? tags[1] : HB_OT_TAG_DEFAULT_SCRIPT; } @@ -231,7 +231,7 @@ hb_ot_tag_from_language (hb_language_t language) { unsigned int count = 1; hb_tag_t tags[1]; - hb_ot_tags (HB_SCRIPT_UNKNOWN, language, nullptr, nullptr, &count, tags); + hb_ot_tags_from_script_and_language (HB_SCRIPT_UNKNOWN, language, nullptr, nullptr, &count, tags); return count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_LANGUAGE; } @@ -307,6 +307,8 @@ parse_private_use_subtag (const char *private_use_subtag, for (; i < 4; i++) tag[i] = ' '; tags[0] = HB_TAG (tag[0], tag[1], tag[2], tag[3]); + if ((tags[0] & 0xDFDFDFDF) == HB_OT_TAG_DEFAULT_SCRIPT) + tags[0] ^= ~0xDFDFDFDF; *count = 1; return false; } @@ -316,7 +318,7 @@ parse_private_use_subtag (const char *private_use_subtag, } /** - * hb_ot_tags: + * hb_ot_tags_from_script_and_language: * @script: an #hb_script_t to convert. * @language: an #hb_language_t to convert. * @script_count: (allow-none): maximum number of script tags to retrieve (IN) @@ -332,12 +334,12 @@ parse_private_use_subtag (const char *private_use_subtag, * **/ void -hb_ot_tags (hb_script_t script, - hb_language_t language, - unsigned int *script_count /* IN/OUT */, - hb_tag_t *script_tags /* OUT */, - unsigned int *language_count /* IN/OUT */, - hb_tag_t *language_tags /* OUT */) +hb_ot_tags_from_script_and_language (hb_script_t script, + hb_language_t language, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */, + unsigned int *language_count /* IN/OUT */, + hb_tag_t *language_tags /* OUT */) { bool needs_script = true; @@ -428,6 +430,61 @@ hb_ot_tag_to_language (hb_tag_t tag) } } +/** + * hb_ot_tags_to_script_and_language: + * @script_tag: a script tag + * @language_tag: a language tag + * @script (allow-none): the #hb_script_t corresponding to @script_tag (OUT). + * @language (allow-none): the #hb_language_t corresponding to @script_tag and + * @language_tag (OUT). + * + * Converts a script tag and a language tag to an #hb_script_t and an + * #hb_language_t. + * + **/ +void +hb_ot_tags_to_script_and_language (hb_tag_t script_tag, + hb_tag_t language_tag, + hb_script_t *script /* OUT */, + hb_language_t *language /* OUT */) +{ + hb_script_t script_out = hb_ot_tag_to_script (script_tag); + if (script) + *script = script_out; + if (language) { + unsigned int script_count = 1; + hb_tag_t primary_script_tag[1]; + hb_ot_tags_from_script_and_language (script_out, HB_LANGUAGE_INVALID, &script_count, primary_script_tag, nullptr, nullptr); + *language = hb_ot_tag_to_language (language_tag); + if (script_count == 0 || primary_script_tag[0] != script_tag) { + unsigned char *buf; + const char *lang_str = hb_language_to_string (*language); + size_t len = strlen (lang_str); + buf = (unsigned char *) malloc (len + 11); + if (unlikely (!buf)) { + *language = nullptr; + } else { + memcpy (buf, lang_str, len); + if (lang_str[0] != 'x' || lang_str[1] != '-') { + buf[len++] = '-'; + buf[len++] = 'x'; + } + buf[len++] = '-'; + buf[len++] = 'h'; + buf[len++] = 'b'; + buf[len++] = 's'; + buf[len++] = 'c'; + buf[len++] = script_tag >> 24; + buf[len++] = (script_tag >> 16) & 0xFF; + buf[len++] = (script_tag >> 8) & 0xFF; + buf[len++] = script_tag & 0xFF; + *language = hb_language_from_string ((char *) buf, len); + free (buf); + } + } + } +} + #ifdef MAIN static inline void test_langs_sorted (void) diff --git a/src/hb-ot-tag.h b/src/hb-ot-tag.h index ef7014c4e..e3523a001 100644 --- a/src/hb-ot-tag.h +++ b/src/hb-ot-tag.h @@ -43,12 +43,12 @@ HB_BEGIN_DECLS #define HB_OT_MAX_TAGS_PER_LANGUAGE 3u HB_EXTERN void -hb_ot_tags (hb_script_t script, - hb_language_t language, - unsigned int *script_count /* IN/OUT */, - hb_tag_t *script_tags /* OUT */, - unsigned int *language_count /* IN/OUT */, - hb_tag_t *language_tags /* OUT */); +hb_ot_tags_from_script_and_language (hb_script_t script, + hb_language_t language, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */, + unsigned int *language_count /* IN/OUT */, + hb_tag_t *language_tags /* OUT */); HB_EXTERN hb_script_t hb_ot_tag_to_script (hb_tag_t tag); @@ -56,6 +56,12 @@ hb_ot_tag_to_script (hb_tag_t tag); HB_EXTERN hb_language_t hb_ot_tag_to_language (hb_tag_t tag); +HB_EXTERN void +hb_ot_tags_to_script_and_language (hb_tag_t script_tag, + hb_tag_t language_tag, + hb_script_t *script /* OUT */, + hb_language_t *language /* OUT */); + HB_END_DECLS diff --git a/test/api/test-ot-tag.c b/test/api/test-ot-tag.c index 350289812..6b968bf3d 100644 --- a/test/api/test-ot-tag.c +++ b/test/api/test-ot-tag.c @@ -60,7 +60,7 @@ test_script_tags_from_language (const char *s, const char *lang_s, hb_script_t s g_test_message ("Testing script %c%c%c%c: script tag %s, language tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s, lang_s); tag = hb_tag_from_string (s, -1); - hb_ot_tags (script, hb_language_from_string (lang_s, -1), &count, &t, NULL, NULL); + hb_ot_tags_from_script_and_language (script, hb_language_from_string (lang_s, -1), &count, &t, NULL, NULL); if (count != 0) { @@ -212,6 +212,31 @@ test_tag_to_language (const char *tag_s, const char *lang_s) g_assert (lang == hb_ot_tag_to_language (tag)); } +static void +test_tags_to_script_and_language (const char *script_tag_s, + const char *lang_tag_s, + const char *script_s, + const char *lang_s) +{ + hb_script_t actual_script[1]; + hb_language_t actual_lang[1]; + hb_tag_t script_tag = hb_tag_from_string (script_tag_s, -1); + hb_tag_t lang_tag = hb_tag_from_string (lang_tag_s, -1); + hb_ot_tags_to_script_and_language (script_tag, lang_tag, actual_script, actual_lang); + g_assert_cmphex (*actual_script, ==, hb_tag_from_string (script_s, -1)); + g_assert_cmpstr (hb_language_to_string (*actual_lang), ==, lang_s); +} + +static void +test_ot_tags_to_script_and_language (void) +{ + test_tags_to_script_and_language ("DFLT", "ENG", "", "en-x-hbscdflt"); + test_tags_to_script_and_language ("latn", "ENG", "Latn", "en"); + test_tags_to_script_and_language ("deva", "MAR", "Deva", "mr-x-hbscdeva"); + test_tags_to_script_and_language ("dev2", "MAR", "Deva", "mr"); + test_tags_to_script_and_language ("qaa", "QTZ0", "Qaaa", "x-hbotqtz0-hbscqaa"); +} + static void test_ot_tag_language (void) { @@ -406,7 +431,7 @@ test_tags (hb_script_t script, hb_language_t lang = hb_language_from_string (lang_s, -1); va_start (expected_tags, expected_language_count); - hb_ot_tags (script, lang, &script_count, script_tags, &language_count, language_tags); + hb_ot_tags_from_script_and_language (script, lang, &script_count, script_tags, &language_count, language_tags); g_assert_cmpuint (script_count, ==, expected_script_count); g_assert_cmpuint (language_count, ==, expected_language_count); @@ -427,11 +452,13 @@ static void test_ot_tag_full (void) { test_tags (HB_SCRIPT_INVALID, "en", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "ENG"); + test_tags (HB_SCRIPT_INVALID, "en-x-hbscdflt", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "DFLT", "ENG"); test_tags (HB_SCRIPT_LATIN, "en", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "latn", "ENG"); test_tags (HB_SCRIPT_LATIN, "en", 0, 0, 0, 0); test_tags (HB_SCRIPT_INVALID, "und-fonnapa", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "APPH"); test_tags (HB_SCRIPT_INVALID, "en-fonnapa", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "APPH"); test_tags (HB_SCRIPT_INVALID, "x-hbot1234-hbsc5678", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "5678", "1234"); + test_tags (HB_SCRIPT_INVALID, "x-hbsc5678-hbot1234", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "5678", "1234"); test_tags (HB_SCRIPT_MALAYALAM, "ml", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 2, 2, "mlm2", "mlym", "MAL", "MLR"); test_tags (HB_SCRIPT_MALAYALAM, "ml", 1, 1, 1, 1, "mlm2", "MAL"); test_tags (HB_SCRIPT_INVALID, "xyz", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "XYZ"); @@ -448,6 +475,8 @@ main (int argc, char **argv) hb_test_add (test_ot_tag_script_from_language); hb_test_add (test_ot_tag_script_indic); + hb_test_add (test_ot_tags_to_script_and_language); + hb_test_add (test_ot_tag_language); hb_test_add (test_ot_tag_full);