michael@0: /* michael@0: * Copyright © 1998-2004 David Turner and Werner Lemberg michael@0: * Copyright © 2006 Behdad Esfahbod michael@0: * Copyright © 2007,2008,2009 Red Hat, Inc. michael@0: * Copyright © 2012,2013 Google, Inc. michael@0: * michael@0: * This is part of HarfBuzz, a text shaping library. michael@0: * michael@0: * Permission is hereby granted, without written agreement and without michael@0: * license or royalty fees, to use, copy, modify, and distribute this michael@0: * software and its documentation for any purpose, provided that the michael@0: * above copyright notice and the following two paragraphs appear in michael@0: * all copies of this software. michael@0: * michael@0: * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR michael@0: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES michael@0: * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN michael@0: * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH michael@0: * DAMAGE. michael@0: * michael@0: * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, michael@0: * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND michael@0: * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS michael@0: * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO michael@0: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. michael@0: * michael@0: * Red Hat Author(s): Behdad Esfahbod michael@0: * Google Author(s): Behdad Esfahbod michael@0: */ michael@0: michael@0: #include "hb-ot-layout-private.hh" michael@0: michael@0: #include "hb-ot-layout-gdef-table.hh" michael@0: #include "hb-ot-layout-gsub-table.hh" michael@0: #include "hb-ot-layout-gpos-table.hh" michael@0: #include "hb-ot-layout-jstf-table.hh" michael@0: michael@0: #include "hb-ot-map-private.hh" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: michael@0: HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) michael@0: michael@0: hb_ot_layout_t * michael@0: _hb_ot_layout_create (hb_face_t *face) michael@0: { michael@0: hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); michael@0: if (unlikely (!layout)) michael@0: return NULL; michael@0: michael@0: layout->gdef_blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_GDEF)); michael@0: layout->gdef = OT::Sanitizer::lock_instance (layout->gdef_blob); michael@0: michael@0: layout->gsub_blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_GSUB)); michael@0: layout->gsub = OT::Sanitizer::lock_instance (layout->gsub_blob); michael@0: michael@0: layout->gpos_blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_GPOS)); michael@0: layout->gpos = OT::Sanitizer::lock_instance (layout->gpos_blob); michael@0: michael@0: layout->gsub_lookup_count = layout->gsub->get_lookup_count (); michael@0: layout->gpos_lookup_count = layout->gpos->get_lookup_count (); michael@0: michael@0: layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); michael@0: layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); michael@0: michael@0: if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) || michael@0: (layout->gpos_lookup_count && !layout->gpos_accels))) michael@0: { michael@0: _hb_ot_layout_destroy (layout); michael@0: return NULL; michael@0: } michael@0: michael@0: for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) michael@0: layout->gsub_accels[i].init (layout->gsub->get_lookup (i)); michael@0: for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) michael@0: layout->gpos_accels[i].init (layout->gpos->get_lookup (i)); michael@0: michael@0: return layout; michael@0: } michael@0: michael@0: void michael@0: _hb_ot_layout_destroy (hb_ot_layout_t *layout) michael@0: { michael@0: for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) michael@0: layout->gsub_accels[i].fini (layout->gsub->get_lookup (i)); michael@0: for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) michael@0: layout->gpos_accels[i].fini (layout->gpos->get_lookup (i)); michael@0: michael@0: free (layout->gsub_accels); michael@0: free (layout->gpos_accels); michael@0: michael@0: hb_blob_destroy (layout->gdef_blob); michael@0: hb_blob_destroy (layout->gsub_blob); michael@0: hb_blob_destroy (layout->gpos_blob); michael@0: michael@0: free (layout); michael@0: } michael@0: michael@0: static inline const OT::GDEF& michael@0: _get_gdef (hb_face_t *face) michael@0: { michael@0: if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF); michael@0: return *hb_ot_layout_from_face (face)->gdef; michael@0: } michael@0: static inline const OT::GSUB& michael@0: _get_gsub (hb_face_t *face) michael@0: { michael@0: if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB); michael@0: return *hb_ot_layout_from_face (face)->gsub; michael@0: } michael@0: static inline const OT::GPOS& michael@0: _get_gpos (hb_face_t *face) michael@0: { michael@0: if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS); michael@0: return *hb_ot_layout_from_face (face)->gpos; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * GDEF michael@0: */ michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_has_glyph_classes (hb_face_t *face) michael@0: { michael@0: return _get_gdef (face).has_glyph_classes (); michael@0: } michael@0: michael@0: hb_ot_layout_glyph_class_t michael@0: hb_ot_layout_get_glyph_class (hb_face_t *face, michael@0: hb_codepoint_t glyph) michael@0: { michael@0: return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph); michael@0: } michael@0: michael@0: void michael@0: hb_ot_layout_get_glyphs_in_class (hb_face_t *face, michael@0: hb_ot_layout_glyph_class_t klass, michael@0: hb_set_t *glyphs /* OUT */) michael@0: { michael@0: return _get_gdef (face).get_glyphs_in_class (klass, glyphs); michael@0: } michael@0: michael@0: unsigned int michael@0: hb_ot_layout_get_attach_points (hb_face_t *face, michael@0: hb_codepoint_t glyph, michael@0: unsigned int start_offset, michael@0: unsigned int *point_count /* IN/OUT */, michael@0: unsigned int *point_array /* OUT */) michael@0: { michael@0: return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); michael@0: } michael@0: michael@0: unsigned int michael@0: hb_ot_layout_get_ligature_carets (hb_font_t *font, michael@0: hb_direction_t direction, michael@0: hb_codepoint_t glyph, michael@0: unsigned int start_offset, michael@0: unsigned int *caret_count /* IN/OUT */, michael@0: int *caret_array /* OUT */) michael@0: { michael@0: return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); michael@0: } michael@0: michael@0: michael@0: /* michael@0: * GSUB/GPOS michael@0: */ michael@0: michael@0: static const OT::GSUBGPOS& michael@0: get_gsubgpos_table (hb_face_t *face, michael@0: hb_tag_t table_tag) michael@0: { michael@0: switch (table_tag) { michael@0: case HB_OT_TAG_GSUB: return _get_gsub (face); michael@0: case HB_OT_TAG_GPOS: return _get_gpos (face); michael@0: default: return OT::Null(OT::GSUBGPOS); michael@0: } michael@0: } michael@0: michael@0: michael@0: unsigned int michael@0: hb_ot_layout_table_get_script_tags (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int start_offset, michael@0: unsigned int *script_count /* IN/OUT */, michael@0: hb_tag_t *script_tags /* OUT */) michael@0: { michael@0: const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); michael@0: michael@0: return g.get_script_tags (start_offset, script_count, script_tags); michael@0: } michael@0: michael@0: #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_table_find_script (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: hb_tag_t script_tag, michael@0: unsigned int *script_index) michael@0: { michael@0: ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); michael@0: const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); michael@0: michael@0: if (g.find_script_index (script_tag, script_index)) michael@0: return true; michael@0: michael@0: /* try finding 'DFLT' */ michael@0: if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) michael@0: return false; michael@0: michael@0: /* try with 'dflt'; MS site has had typos and many fonts use it now :(. michael@0: * including many versions of DejaVu Sans Mono! */ michael@0: if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) michael@0: return false; michael@0: michael@0: /* try with 'latn'; some old fonts put their features there even though michael@0: they're really trying to support Thai, for example :( */ michael@0: if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) michael@0: return false; michael@0: michael@0: if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; michael@0: return false; michael@0: } michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_table_choose_script (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: const hb_tag_t *script_tags, michael@0: unsigned int *script_index, michael@0: hb_tag_t *chosen_script) michael@0: { michael@0: ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); michael@0: const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); michael@0: michael@0: while (*script_tags) michael@0: { michael@0: if (g.find_script_index (*script_tags, script_index)) { michael@0: if (chosen_script) michael@0: *chosen_script = *script_tags; michael@0: return true; michael@0: } michael@0: script_tags++; michael@0: } michael@0: michael@0: /* try finding 'DFLT' */ michael@0: if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { michael@0: if (chosen_script) michael@0: *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; michael@0: return false; michael@0: } michael@0: michael@0: /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ michael@0: if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { michael@0: if (chosen_script) michael@0: *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; michael@0: return false; michael@0: } michael@0: michael@0: /* try with 'latn'; some old fonts put their features there even though michael@0: they're really trying to support Thai, for example :( */ michael@0: if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { michael@0: if (chosen_script) michael@0: *chosen_script = HB_OT_TAG_LATIN_SCRIPT; michael@0: return false; michael@0: } michael@0: michael@0: if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; michael@0: if (chosen_script) michael@0: *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; michael@0: return false; michael@0: } michael@0: michael@0: unsigned int michael@0: hb_ot_layout_table_get_feature_tags (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int start_offset, michael@0: unsigned int *feature_count /* IN/OUT */, michael@0: hb_tag_t *feature_tags /* OUT */) michael@0: { michael@0: const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); michael@0: michael@0: return g.get_feature_tags (start_offset, feature_count, feature_tags); michael@0: } michael@0: michael@0: michael@0: unsigned int michael@0: hb_ot_layout_script_get_language_tags (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int script_index, michael@0: unsigned int start_offset, michael@0: unsigned int *language_count /* IN/OUT */, michael@0: hb_tag_t *language_tags /* OUT */) michael@0: { michael@0: const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); michael@0: michael@0: return s.get_lang_sys_tags (start_offset, language_count, language_tags); michael@0: } michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_script_find_language (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int script_index, michael@0: hb_tag_t language_tag, michael@0: unsigned int *language_index) michael@0: { michael@0: ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); michael@0: const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); michael@0: michael@0: if (s.find_lang_sys_index (language_tag, language_index)) michael@0: return true; michael@0: michael@0: /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ michael@0: if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) michael@0: return false; michael@0: michael@0: if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; michael@0: return false; michael@0: } michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_language_get_required_feature_index (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int script_index, michael@0: unsigned int language_index, michael@0: unsigned int *feature_index) michael@0: { michael@0: const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); michael@0: michael@0: if (feature_index) *feature_index = l.get_required_feature_index (); michael@0: michael@0: return l.has_required_feature (); michael@0: } michael@0: michael@0: unsigned int michael@0: hb_ot_layout_language_get_feature_indexes (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int script_index, michael@0: unsigned int language_index, michael@0: unsigned int start_offset, michael@0: unsigned int *feature_count /* IN/OUT */, michael@0: unsigned int *feature_indexes /* OUT */) michael@0: { michael@0: const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); michael@0: const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); michael@0: michael@0: return l.get_feature_indexes (start_offset, feature_count, feature_indexes); michael@0: } michael@0: michael@0: unsigned int michael@0: hb_ot_layout_language_get_feature_tags (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int script_index, michael@0: unsigned int language_index, michael@0: unsigned int start_offset, michael@0: unsigned int *feature_count /* IN/OUT */, michael@0: hb_tag_t *feature_tags /* OUT */) michael@0: { michael@0: const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); michael@0: const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); michael@0: michael@0: ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); michael@0: unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); michael@0: michael@0: if (feature_tags) { michael@0: unsigned int count = *feature_count; michael@0: for (unsigned int i = 0; i < count; i++) michael@0: feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); michael@0: } michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_language_find_feature (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int script_index, michael@0: unsigned int language_index, michael@0: hb_tag_t feature_tag, michael@0: unsigned int *feature_index) michael@0: { michael@0: ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); michael@0: const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); michael@0: const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); michael@0: michael@0: unsigned int num_features = l.get_feature_count (); michael@0: for (unsigned int i = 0; i < num_features; i++) { michael@0: unsigned int f_index = l.get_feature_index (i); michael@0: michael@0: if (feature_tag == g.get_feature_tag (f_index)) { michael@0: if (feature_index) *feature_index = f_index; michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; michael@0: return false; michael@0: } michael@0: michael@0: unsigned int michael@0: hb_ot_layout_feature_get_lookups (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int feature_index, michael@0: unsigned int start_offset, michael@0: unsigned int *lookup_count /* IN/OUT */, michael@0: unsigned int *lookup_indexes /* OUT */) michael@0: { michael@0: const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); michael@0: const OT::Feature &f = g.get_feature (feature_index); michael@0: michael@0: return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); michael@0: } michael@0: michael@0: unsigned int michael@0: hb_ot_layout_table_get_lookup_count (hb_face_t *face, michael@0: hb_tag_t table_tag) michael@0: { michael@0: switch (table_tag) michael@0: { michael@0: case HB_OT_TAG_GSUB: michael@0: { michael@0: return hb_ot_layout_from_face (face)->gsub_lookup_count; michael@0: } michael@0: case HB_OT_TAG_GPOS: michael@0: { michael@0: return hb_ot_layout_from_face (face)->gpos_lookup_count; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: static void michael@0: _hb_ot_layout_collect_lookups_lookups (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int feature_index, michael@0: hb_set_t *lookup_indexes /* OUT */) michael@0: { michael@0: unsigned int lookup_indices[32]; michael@0: unsigned int offset, len; michael@0: michael@0: offset = 0; michael@0: do { michael@0: len = ARRAY_LENGTH (lookup_indices); michael@0: hb_ot_layout_feature_get_lookups (face, michael@0: table_tag, michael@0: feature_index, michael@0: offset, &len, michael@0: lookup_indices); michael@0: michael@0: for (unsigned int i = 0; i < len; i++) michael@0: lookup_indexes->add (lookup_indices[i]); michael@0: michael@0: offset += len; michael@0: } while (len == ARRAY_LENGTH (lookup_indices)); michael@0: } michael@0: michael@0: static void michael@0: _hb_ot_layout_collect_lookups_features (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int script_index, michael@0: unsigned int language_index, michael@0: const hb_tag_t *features, michael@0: hb_set_t *lookup_indexes /* OUT */) michael@0: { michael@0: if (!features) michael@0: { michael@0: unsigned int required_feature_index; michael@0: if (hb_ot_layout_language_get_required_feature_index (face, michael@0: table_tag, michael@0: script_index, michael@0: language_index, michael@0: &required_feature_index)) michael@0: _hb_ot_layout_collect_lookups_lookups (face, michael@0: table_tag, michael@0: required_feature_index, michael@0: lookup_indexes); michael@0: michael@0: /* All features */ michael@0: unsigned int feature_indices[32]; michael@0: unsigned int offset, len; michael@0: michael@0: offset = 0; michael@0: do { michael@0: len = ARRAY_LENGTH (feature_indices); michael@0: hb_ot_layout_language_get_feature_indexes (face, michael@0: table_tag, michael@0: script_index, michael@0: language_index, michael@0: offset, &len, michael@0: feature_indices); michael@0: michael@0: for (unsigned int i = 0; i < len; i++) michael@0: _hb_ot_layout_collect_lookups_lookups (face, michael@0: table_tag, michael@0: feature_indices[i], michael@0: lookup_indexes); michael@0: michael@0: offset += len; michael@0: } while (len == ARRAY_LENGTH (feature_indices)); michael@0: } michael@0: else michael@0: { michael@0: for (; *features; features++) michael@0: { michael@0: unsigned int feature_index; michael@0: if (hb_ot_layout_language_find_feature (face, michael@0: table_tag, michael@0: script_index, michael@0: language_index, michael@0: *features, michael@0: &feature_index)) michael@0: _hb_ot_layout_collect_lookups_lookups (face, michael@0: table_tag, michael@0: feature_index, michael@0: lookup_indexes); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void michael@0: _hb_ot_layout_collect_lookups_languages (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int script_index, michael@0: const hb_tag_t *languages, michael@0: const hb_tag_t *features, michael@0: hb_set_t *lookup_indexes /* OUT */) michael@0: { michael@0: _hb_ot_layout_collect_lookups_features (face, michael@0: table_tag, michael@0: script_index, michael@0: HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, michael@0: features, michael@0: lookup_indexes); michael@0: michael@0: if (!languages) michael@0: { michael@0: /* All languages */ michael@0: unsigned int count = hb_ot_layout_script_get_language_tags (face, michael@0: table_tag, michael@0: script_index, michael@0: 0, NULL, NULL); michael@0: for (unsigned int language_index = 0; language_index < count; language_index++) michael@0: _hb_ot_layout_collect_lookups_features (face, michael@0: table_tag, michael@0: script_index, michael@0: language_index, michael@0: features, michael@0: lookup_indexes); michael@0: } michael@0: else michael@0: { michael@0: for (; *languages; languages++) michael@0: { michael@0: unsigned int language_index; michael@0: if (hb_ot_layout_script_find_language (face, michael@0: table_tag, michael@0: script_index, michael@0: *languages, michael@0: &language_index)) michael@0: _hb_ot_layout_collect_lookups_features (face, michael@0: table_tag, michael@0: script_index, michael@0: language_index, michael@0: features, michael@0: lookup_indexes); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: hb_ot_layout_collect_lookups (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: const hb_tag_t *scripts, michael@0: const hb_tag_t *languages, michael@0: const hb_tag_t *features, michael@0: hb_set_t *lookup_indexes /* OUT */) michael@0: { michael@0: if (!scripts) michael@0: { michael@0: /* All scripts */ michael@0: unsigned int count = hb_ot_layout_table_get_script_tags (face, michael@0: table_tag, michael@0: 0, NULL, NULL); michael@0: for (unsigned int script_index = 0; script_index < count; script_index++) michael@0: _hb_ot_layout_collect_lookups_languages (face, michael@0: table_tag, michael@0: script_index, michael@0: languages, michael@0: features, michael@0: lookup_indexes); michael@0: } michael@0: else michael@0: { michael@0: for (; *scripts; scripts++) michael@0: { michael@0: unsigned int script_index; michael@0: if (hb_ot_layout_table_find_script (face, michael@0: table_tag, michael@0: *scripts, michael@0: &script_index)) michael@0: _hb_ot_layout_collect_lookups_languages (face, michael@0: table_tag, michael@0: script_index, michael@0: languages, michael@0: features, michael@0: lookup_indexes); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, michael@0: hb_tag_t table_tag, michael@0: unsigned int lookup_index, michael@0: hb_set_t *glyphs_before, /* OUT. May be NULL */ michael@0: hb_set_t *glyphs_input, /* OUT. May be NULL */ michael@0: hb_set_t *glyphs_after, /* OUT. May be NULL */ michael@0: hb_set_t *glyphs_output /* OUT. May be NULL */) michael@0: { michael@0: if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return; michael@0: michael@0: OT::hb_collect_glyphs_context_t c (face, michael@0: glyphs_before, michael@0: glyphs_input, michael@0: glyphs_after, michael@0: glyphs_output); michael@0: michael@0: switch (table_tag) michael@0: { michael@0: case HB_OT_TAG_GSUB: michael@0: { michael@0: const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); michael@0: l.collect_glyphs (&c); michael@0: return; michael@0: } michael@0: case HB_OT_TAG_GPOS: michael@0: { michael@0: const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index); michael@0: l.collect_glyphs (&c); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: /* michael@0: * OT::GSUB michael@0: */ michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_has_substitution (hb_face_t *face) michael@0: { michael@0: return &_get_gsub (face) != &OT::Null(OT::GSUB); michael@0: } michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_lookup_would_substitute (hb_face_t *face, michael@0: unsigned int lookup_index, michael@0: const hb_codepoint_t *glyphs, michael@0: unsigned int glyphs_length, michael@0: hb_bool_t zero_context) michael@0: { michael@0: if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false; michael@0: return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context); michael@0: } michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, michael@0: unsigned int lookup_index, michael@0: const hb_codepoint_t *glyphs, michael@0: unsigned int glyphs_length, michael@0: hb_bool_t zero_context) michael@0: { michael@0: if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false; michael@0: OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context); michael@0: michael@0: const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); michael@0: michael@0: return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index].digest); michael@0: } michael@0: michael@0: void michael@0: hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) michael@0: { michael@0: OT::GSUB::substitute_start (font, buffer); michael@0: } michael@0: michael@0: void michael@0: hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer) michael@0: { michael@0: OT::GSUB::substitute_finish (font, buffer); michael@0: } michael@0: michael@0: void michael@0: hb_ot_layout_lookup_substitute_closure (hb_face_t *face, michael@0: unsigned int lookup_index, michael@0: hb_set_t *glyphs) michael@0: { michael@0: OT::hb_closure_context_t c (face, glyphs); michael@0: michael@0: const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index); michael@0: michael@0: l.closure (&c); michael@0: } michael@0: michael@0: /* michael@0: * OT::GPOS michael@0: */ michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_has_positioning (hb_face_t *face) michael@0: { michael@0: return &_get_gpos (face) != &OT::Null(OT::GPOS); michael@0: } michael@0: michael@0: void michael@0: hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) michael@0: { michael@0: OT::GPOS::position_start (font, buffer); michael@0: } michael@0: michael@0: void michael@0: hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer) michael@0: { michael@0: OT::GPOS::position_finish (font, buffer); michael@0: } michael@0: michael@0: hb_bool_t michael@0: hb_ot_layout_get_size_params (hb_face_t *face, michael@0: unsigned int *design_size, /* OUT. May be NULL */ michael@0: unsigned int *subfamily_id, /* OUT. May be NULL */ michael@0: unsigned int *subfamily_name_id, /* OUT. May be NULL */ michael@0: unsigned int *range_start, /* OUT. May be NULL */ michael@0: unsigned int *range_end /* OUT. May be NULL */) michael@0: { michael@0: const OT::GPOS &gpos = _get_gpos (face); michael@0: const hb_tag_t tag = HB_TAG ('s','i','z','e'); michael@0: michael@0: unsigned int num_features = gpos.get_feature_count (); michael@0: for (unsigned int i = 0; i < num_features; i++) michael@0: { michael@0: if (tag == gpos.get_feature_tag (i)) michael@0: { michael@0: const OT::Feature &f = gpos.get_feature (i); michael@0: const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); michael@0: michael@0: if (params.designSize) michael@0: { michael@0: #define PARAM(a, A) if (a) *a = params.A michael@0: PARAM (design_size, designSize); michael@0: PARAM (subfamily_id, subfamilyID); michael@0: PARAM (subfamily_name_id, subfamilyNameID); michael@0: PARAM (range_start, rangeStart); michael@0: PARAM (range_end, rangeEnd); michael@0: #undef PARAM michael@0: michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: #define PARAM(a, A) if (a) *a = 0 michael@0: PARAM (design_size, designSize); michael@0: PARAM (subfamily_id, subfamilyID); michael@0: PARAM (subfamily_name_id, subfamilyNameID); michael@0: PARAM (range_start, rangeStart); michael@0: PARAM (range_end, rangeEnd); michael@0: #undef PARAM michael@0: michael@0: return false; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Parts of different types are implemented here such that they have direct michael@0: * access to GSUB/GPOS lookups. michael@0: */ michael@0: michael@0: michael@0: struct GSUBProxy michael@0: { michael@0: static const unsigned int table_index = 0; michael@0: static const bool inplace = false; michael@0: typedef OT::SubstLookup Lookup; michael@0: michael@0: GSUBProxy (hb_face_t *face) : michael@0: table (*hb_ot_layout_from_face (face)->gsub), michael@0: accels (hb_ot_layout_from_face (face)->gsub_accels) {} michael@0: michael@0: const OT::GSUB &table; michael@0: const hb_ot_layout_lookup_accelerator_t *accels; michael@0: }; michael@0: michael@0: struct GPOSProxy michael@0: { michael@0: static const unsigned int table_index = 1; michael@0: static const bool inplace = true; michael@0: typedef OT::PosLookup Lookup; michael@0: michael@0: GPOSProxy (hb_face_t *face) : michael@0: table (*hb_ot_layout_from_face (face)->gpos), michael@0: accels (hb_ot_layout_from_face (face)->gpos_accels) {} michael@0: michael@0: const OT::GPOS &table; michael@0: const hb_ot_layout_lookup_accelerator_t *accels; michael@0: }; michael@0: michael@0: michael@0: template michael@0: static inline bool apply_once (OT::hb_apply_context_t *c, michael@0: const Lookup &lookup) michael@0: { michael@0: if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props)) michael@0: return false; michael@0: return lookup.dispatch (c); michael@0: } michael@0: michael@0: template michael@0: static inline bool michael@0: apply_string (OT::hb_apply_context_t *c, michael@0: const typename Proxy::Lookup &lookup, michael@0: const hb_ot_layout_lookup_accelerator_t &accel) michael@0: { michael@0: bool ret = false; michael@0: hb_buffer_t *buffer = c->buffer; michael@0: michael@0: if (unlikely (!buffer->len || !c->lookup_mask)) michael@0: return false; michael@0: michael@0: c->set_lookup (lookup); michael@0: michael@0: if (likely (!lookup.is_reverse ())) michael@0: { michael@0: /* in/out forward substitution/positioning */ michael@0: if (Proxy::table_index == 0) michael@0: buffer->clear_output (); michael@0: buffer->idx = 0; michael@0: michael@0: while (buffer->idx < buffer->len) michael@0: { michael@0: if (accel.digest.may_have (buffer->cur().codepoint) && michael@0: (buffer->cur().mask & c->lookup_mask) && michael@0: apply_once (c, lookup)) michael@0: ret = true; michael@0: else michael@0: buffer->next_glyph (); michael@0: } michael@0: if (ret) michael@0: { michael@0: if (!Proxy::inplace) michael@0: buffer->swap_buffers (); michael@0: else michael@0: assert (!buffer->has_separate_output ()); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: /* in-place backward substitution/positioning */ michael@0: if (Proxy::table_index == 0) michael@0: buffer->remove_output (); michael@0: buffer->idx = buffer->len - 1; michael@0: do michael@0: { michael@0: if (accel.digest.may_have (buffer->cur().codepoint) && michael@0: (buffer->cur().mask & c->lookup_mask) && michael@0: apply_once (c, lookup)) michael@0: ret = true; michael@0: /* The reverse lookup doesn't "advance" cursor (for good reason). */ michael@0: buffer->idx--; michael@0: michael@0: } michael@0: while ((int) buffer->idx >= 0); michael@0: } michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: template michael@0: inline void hb_ot_map_t::apply (const Proxy &proxy, michael@0: const hb_ot_shape_plan_t *plan, michael@0: hb_font_t *font, michael@0: hb_buffer_t *buffer) const michael@0: { michael@0: const unsigned int table_index = proxy.table_index; michael@0: unsigned int i = 0; michael@0: OT::hb_apply_context_t c (table_index, font, buffer); michael@0: c.set_recurse_func (Proxy::Lookup::apply_recurse_func); michael@0: michael@0: for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) { michael@0: const stage_map_t *stage = &stages[table_index][stage_index]; michael@0: for (; i < stage->last_lookup; i++) michael@0: { michael@0: unsigned int lookup_index = lookups[table_index][i].index; michael@0: c.set_lookup_mask (lookups[table_index][i].mask); michael@0: c.set_auto_zwj (lookups[table_index][i].auto_zwj); michael@0: apply_string (&c, michael@0: proxy.table.get_lookup (lookup_index), michael@0: proxy.accels[lookup_index]); michael@0: } michael@0: michael@0: if (stage->pause_func) michael@0: { michael@0: buffer->clear_output (); michael@0: stage->pause_func (plan, font, buffer); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const michael@0: { michael@0: GSUBProxy proxy (font->face); michael@0: apply (proxy, plan, font, buffer); michael@0: } michael@0: michael@0: void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const michael@0: { michael@0: GPOSProxy proxy (font->face); michael@0: apply (proxy, plan, font, buffer); michael@0: } michael@0: michael@0: HB_INTERNAL void michael@0: hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c, michael@0: const OT::SubstLookup &lookup, michael@0: const hb_ot_layout_lookup_accelerator_t &accel) michael@0: { michael@0: apply_string (c, lookup, accel); michael@0: }