diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index 9472af4f1..fccfcb0ed 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -479,7 +479,9 @@ HB_OT_TAG_GSUB HB_OT_TAG_JSTF hb_ot_layout_collect_lookups hb_ot_layout_collect_features +hb_ot_layout_feature_get_characters hb_ot_layout_feature_get_lookups +hb_ot_layout_feature_get_name_ids hb_ot_layout_feature_with_variations_get_lookups hb_ot_layout_get_attach_points hb_ot_layout_get_glyph_class diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index e5e996d4d..bf0a9c1ed 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -521,6 +521,20 @@ struct FeatureParams return Null(FeatureParamsSize); } + inline const FeatureParamsStylisticSet& get_stylistic_set_params (hb_tag_t tag) const + { + if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return u.stylisticSet; + return Null(FeatureParamsStylisticSet); + } + + inline const FeatureParamsCharacterVariants& get_character_variants_params (hb_tag_t tag) const + { + if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return u.characterVariants; + return Null(FeatureParamsCharacterVariants); + } + private: union { FeatureParamsSize size; diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc index aa36954d0..4c751e641 100644 --- a/src/hb-ot-layout.cc +++ b/src/hb-ot-layout.cc @@ -1116,6 +1116,139 @@ hb_ot_layout_get_size_params (hb_face_t *face, return false; } +/** + * hb_ot_layout_feature_get_name_ids: + * @face: #hb_face_t to work upon + * @table_tag: + * @feature_index: + * @feature_tag: ssXX and cvXX tag + * @label_id: (out) (allow-none): The ‘name’ table name ID that specifies a string + * for a user-interface label for this feature. (May be NULL.) + * @tooltip_id: (out) (allow-none): The ‘name’ table name ID that specifies a string + * that an application can use for tooltip text for this + * feature. (May be NULL.) + * @sample_id: (out) (allow-none): The ‘name’ table name ID that specifies sample text + * that illustrates the effect of this feature. (May be NULL.) + * @num_named_parameters: (out) (allow-none): Number of named parameters. (May be zero.) + * @first_param_id: (out) (allow-none): The first ‘name’ table name ID used to specify + * strings for user-interface labels for the feature + * parameters. (Must be zero if numParameters is zero.) + * + * Return value: true if could find any feature with the tag, false otherwise + * + * Since: REPLACEME + **/ +hb_bool_t +hb_ot_layout_feature_get_name_ids (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + hb_tag_t feature_tag, + hb_name_id_t *label_id, /* OUT. May be NULL */ + hb_name_id_t *tooltip_id, /* OUT. May be NULL */ + hb_name_id_t *sample_id, /* OUT. May be NULL */ + unsigned int *num_named_parameters, /* OUT. May be NULL */ + hb_name_id_t *first_param_id /* OUT. May be NULL */) +{ + static_assert ((OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX), ""); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + const OT::Feature &f = g.get_feature (feature_index); + + const OT::FeatureParams &feature_params = f.get_feature_params (); + if (&feature_params != &Null (OT::FeatureParams)) + { + const OT::FeatureParamsStylisticSet& ss_params = + feature_params.get_stylistic_set_params (feature_tag); + if (&ss_params != &Null (OT::FeatureParamsStylisticSet)) /* ssXX */ + { +#define PARAM(a, A) if (a) *a = A + PARAM(label_id, ss_params.uiNameID); + // ssXX features don't have the rest + PARAM(tooltip_id, HB_NAME_ID_INVALID); + PARAM(sample_id, HB_NAME_ID_INVALID); + PARAM(num_named_parameters, 0); + PARAM(first_param_id, HB_NAME_ID_INVALID); + return true; + } + const OT::FeatureParamsCharacterVariants& cv_params = + feature_params.get_character_variants_params (feature_tag); + if (&cv_params != &Null (OT::FeatureParamsCharacterVariants)) /* cvXX */ + { + PARAM(label_id, cv_params.featUILableNameID); + PARAM(tooltip_id, cv_params.featUITooltipTextNameID); + PARAM(sample_id, cv_params.sampleTextNameID); + PARAM(num_named_parameters, cv_params.numNamedParameters); + PARAM(first_param_id, cv_params.firstParamUILabelNameID); + return true; + } + } + + PARAM(label_id, HB_NAME_ID_INVALID); + PARAM(tooltip_id, HB_NAME_ID_INVALID); + PARAM(sample_id, HB_NAME_ID_INVALID); + PARAM(num_named_parameters, 0); + PARAM(first_param_id, HB_NAME_ID_INVALID); +#undef PARAM + return false; +} + +/** + * hb_ot_layout_feature_get_characters:: + * @face: #hb_face_t to work upon + * @feature_tag: cvXX tag + * @table_tag: + * @feature_index: + * @start_offset: In case the resulting char_count was equal to its input value, there + * is a chance there were more characters on the tag so this API can be + * called with an offset till resulting char_count gets to a number + * lower than input buffer (or consider using just a bigger buffer for + * one shot copying). + * @char_count: (in/out) (allow-none): The count of characters for which this feature + * provides glyph variants. (May be zero.) + * @characters: (out) (allow-none): A buffer pointer. The Unicode Scalar Value + * of the characters for which this feature provides glyph variants. + * + * Return value: Number of total sample characters in the cvXX feature. + * + * Since: REPLACEME + **/ +unsigned int +hb_ot_layout_feature_get_characters (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + hb_tag_t feature_tag, + unsigned int start_offset, + unsigned int *char_count, /* IN/OUT. May be NULL */ + hb_codepoint_t *characters /* OUT. May be NULL */) +{ + static_assert ((OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX), ""); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + const OT::Feature &f = g.get_feature (feature_index); + + const OT::FeatureParams &feature_params = f.get_feature_params (); + + const OT::FeatureParamsCharacterVariants& cv_params = + feature_params.get_character_variants_params(feature_tag); + if (&cv_params != &Null (OT::FeatureParamsCharacterVariants)) + { + unsigned int len = 0; + if (char_count && characters && start_offset < cv_params.characters.len) + { + len = MIN (cv_params.characters.len - start_offset, *char_count); + for (unsigned int i = 0; i < len; ++i) + characters[i] = cv_params.characters[start_offset + i]; + } +#define PARAM(a, A) if (a) *a = A + PARAM(char_count, len); + return cv_params.characters.len; + } + PARAM(char_count, 0); + PARAM(characters, 0); +#undef PARAM + return 0; +} + /* * Parts of different types are implemented here such that they have direct diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h index eb0df8936..0be85cb2e 100644 --- a/src/hb-ot-layout.h +++ b/src/hb-ot-layout.h @@ -330,6 +330,35 @@ hb_ot_layout_get_size_params (hb_face_t *face, unsigned int *range_start, /* OUT. May be NULL */ unsigned int *range_end /* OUT. May be NULL */); +/** + * hb_name_id_t: + * + * Since: REPLACEME + */ +typedef unsigned int hb_name_id_t; + +#define HB_NAME_ID_INVALID 0xFFFF + +HB_EXTERN hb_bool_t +hb_ot_layout_feature_get_name_ids (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + hb_tag_t feature_tag, + hb_name_id_t *label_id /* OUT. May be NULL */, + hb_name_id_t *tooltip_id /* OUT. May be NULL */, + hb_name_id_t *sample_id /* OUT. May be NULL */, + unsigned int *num_named_parameters /* OUT. May be NULL */, + hb_name_id_t *first_param_id /* OUT. May be NULL */); + + +HB_EXTERN unsigned int +hb_ot_layout_feature_get_characters (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + hb_tag_t feature_tag, + unsigned int start_offset, + unsigned int *char_count /* IN/OUT. May be NULL */, + hb_codepoint_t *characters /* OUT. May be NULL */); /* * BASE diff --git a/test/api/CMakeLists.txt b/test/api/CMakeLists.txt index b540eb505..77cb35771 100644 --- a/test/api/CMakeLists.txt +++ b/test/api/CMakeLists.txt @@ -3,6 +3,8 @@ if (HB_HAVE_GLIB) extract_make_variable (TEST_PROGS ${MAKEFILEAM}) list (APPEND TEST_PROGS + test-ot-color + test-ot-nameid test-ot-tag test-c test-cplusplus diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 3ff7f5a8a..0e3f9a406 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -71,6 +71,7 @@ endif TEST_PROGS += \ test-ot-color \ + test-ot-nameid \ test-ot-tag \ $(NULL) diff --git a/test/api/fonts/cv01.otf b/test/api/fonts/cv01.otf new file mode 100644 index 000000000..01dbf01f3 Binary files /dev/null and b/test/api/fonts/cv01.otf differ diff --git a/test/api/test-ot-nameid.c b/test/api/test-ot-nameid.c new file mode 100644 index 000000000..1389129fe --- /dev/null +++ b/test/api/test-ot-nameid.c @@ -0,0 +1,96 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#include +#include +#include + +static const char *font_path = "fonts/cv01.otf"; + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + +#if GLIB_CHECK_VERSION(2,37,2) + gchar *default_path = g_test_build_filename (G_TEST_DIST, font_path, NULL); +#else + gchar *default_path = g_strdup (font_path); +#endif + + hb_blob_t *blob; + hb_face_t *face; + hb_font_t *font; + + char *path = argc > 1 && *argv[1] ? argv[1] : (char *) default_path; + blob = hb_blob_create_from_file (path); + if (hb_blob_get_length (blob) == 0) + g_error ("Font not found."); + + face = hb_face_create (blob, 0); + font = hb_font_create (face); + + hb_tag_t cv01 = HB_TAG ('c','v','0','1'); + unsigned int feature_index = 0; + // FIXME: See why below doesn't work + // if (!hb_ot_layout_language_find_feature (face, HB_OT_TAG_GSUB, 0, 0, cv01, &feature_index)) + // g_error ("Failed to find feature index"); + + hb_name_id_t label_id; + hb_name_id_t tooltip_id; + hb_name_id_t sample_id; + unsigned int num_named_parameters; + hb_name_id_t first_param_id; + if (!hb_ot_layout_feature_get_name_ids (face, HB_OT_TAG_GSUB, feature_index, + cv01, &label_id, &tooltip_id, &sample_id, + &num_named_parameters, &first_param_id)) + g_error ("Failed to get name ids"); + + g_assert (label_id == 256); + g_assert (tooltip_id == 257); + g_assert (sample_id == 258); + g_assert (num_named_parameters == 2); + g_assert (first_param_id == 259); + + hb_codepoint_t characters[100]; + unsigned int char_count = 100; + + unsigned int all_chars; + all_chars = hb_ot_layout_feature_get_characters (face, HB_OT_TAG_GSUB, feature_index, + cv01, 0, &char_count, characters); + + g_assert (all_chars == 2); + g_assert (char_count == 2); + g_assert (characters[0] == 10); + g_assert (characters[1] == 24030); + + hb_font_destroy (font); + hb_face_destroy (face); + hb_blob_destroy (blob); + + g_free (default_path); + + return 0; +}