diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh index 3ea43a0220e80a766284d26b41f267b86ec9abe1..9a44bd59dcce751b5852be590004a03a916c7dc8 100644 --- a/src/hb-ot-map-private.hh +++ b/src/hb-ot-map-private.hh @@ -44,6 +44,9 @@ struct hb_ot_map_t public: + typedef void (*gsub_pause_func_t) (const hb_ot_map_t *map, hb_face_t *face, hb_buffer_t *buffer, void *user_data); + typedef void (*gpos_pause_func_t) (const hb_ot_map_t *map, hb_font_t *font, hb_buffer_t *buffer, void *user_data); + inline hb_mask_t get_global_mask (void) const { return global_mask; } inline hb_mask_t get_mask (hb_tag_t tag, unsigned int *shift = NULL) const { @@ -57,27 +60,23 @@ struct hb_ot_map_t return map ? map->_1_mask : 0; } - inline void substitute (hb_face_t *face, hb_buffer_t *buffer) const { - for (unsigned int i = 0; i < lookups[0].len; i++) - hb_ot_layout_substitute_lookup (face, buffer, lookups[0][i].index, lookups[0][i].mask); - } - - inline void position (hb_font_t *font, hb_buffer_t *buffer) const { - for (unsigned int i = 0; i < lookups[1].len; i++) - hb_ot_layout_position_lookup (font, buffer, lookups[1][i].index, lookups[1][i].mask); - } + HB_INTERNAL void substitute (hb_face_t *face, hb_buffer_t *buffer) const; + HB_INTERNAL void position (hb_font_t *font, hb_buffer_t *buffer) const; inline void finish (void) { features.finish (); lookups[0].finish (); lookups[1].finish (); + pauses[0].finish (); + pauses[1].finish (); } private: struct feature_map_t { hb_tag_t tag; /* should be first for our bsearch to work */ - unsigned int index[2]; /* GSUB, GPOS */ + unsigned int index[2]; /* GSUB/GPOS */ + unsigned int stage[2]; /* GSUB/GPOS */ unsigned int shift; hb_mask_t mask; hb_mask_t _1_mask; /* mask for value=1, for quick access */ @@ -94,6 +93,21 @@ struct hb_ot_map_t { return a->index < b->index ? -1 : a->index > b->index ? 1 : 0; } }; + typedef union { + void *p; + gsub_pause_func_t gsub; + gpos_pause_func_t gpos; + } pause_func_t; + typedef struct { + pause_func_t func; + void *user_data; + } pause_callback_t; + + struct pause_map_t { + unsigned int num_lookups; /* Cumulative */ + pause_callback_t callback; + }; + HB_INTERNAL void add_lookups (hb_face_t *face, unsigned int table_index, unsigned int feature_index, @@ -104,6 +118,7 @@ struct hb_ot_map_t hb_prealloced_array_t features; hb_prealloced_array_t lookups[2]; /* GSUB/GPOS */ + hb_prealloced_array_t pauses[2]; /* GSUB/GPOS */ }; @@ -116,12 +131,17 @@ struct hb_ot_map_builder_t inline void add_bool_feature (hb_tag_t tag, bool global = true) { add_feature (tag, 1, global); } + HB_INTERNAL void add_gsub_pause (hb_ot_map_t::gsub_pause_func_t pause_func, void *user_data); + HB_INTERNAL void add_gpos_pause (hb_ot_map_t::gpos_pause_func_t pause_func, void *user_data); + HB_INTERNAL void compile (hb_face_t *face, const hb_segment_properties_t *props, struct hb_ot_map_t &m); inline void finish (void) { feature_infos.finish (); + pauses[0].finish (); + pauses[1].finish (); } private: @@ -132,12 +152,20 @@ struct hb_ot_map_builder_t unsigned int max_value; bool global; /* whether the feature applies value to every glyph in the buffer */ unsigned int default_value; /* for non-global features, what should the unset glyphs take */ + unsigned int stage[2]; /* GSUB/GPOS */ static int cmp (const feature_info_t *a, const feature_info_t *b) { return (a->tag != b->tag) ? (a->tag < b->tag ? -1 : 1) : (a->seq < b->seq ? -1 : 1); } }; - hb_prealloced_array_t feature_infos; /* used before compile() only */ + struct pause_info_t { + unsigned int stage; + hb_ot_map_t::pause_callback_t callback; + }; + + unsigned int current_stage[2]; /* GSUB/GPOS */ + hb_prealloced_array_t feature_infos; + hb_prealloced_array_t pauses[2]; /* GSUB/GPOS */ }; diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc index a68f1237e232a827a4240c7a84c37ddc4b5121ee..782c496dca68e0e33aa21fa31e0b26bcc5df6fae 100644 --- a/src/hb-ot-map.cc +++ b/src/hb-ot-map.cc @@ -73,6 +73,76 @@ void hb_ot_map_builder_t::add_feature (hb_tag_t tag, unsigned int value, bool gl info->max_value = value; info->global = global; info->default_value = global ? value : 0; + info->stage[0] = current_stage[0]; + info->stage[1] = current_stage[1]; +} + + +void hb_ot_map_t::substitute (hb_face_t *face, hb_buffer_t *buffer) const { + unsigned int table_index = 0; + unsigned int i = 0; + + for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) { + const pause_map_t *pause = &pauses[table_index][pause_index]; + for (; i < pause->num_lookups; i++) + hb_ot_layout_substitute_lookup (face, buffer, lookups[table_index][i].index, lookups[table_index][i].mask); + + pause->callback.func.gsub (this, face, buffer, pause->callback.user_data); + } + + for (; i < lookups[table_index].len; i++) + hb_ot_layout_substitute_lookup (face, buffer, lookups[table_index][i].index, lookups[table_index][i].mask); +} + +void hb_ot_map_t::position (hb_font_t *font, hb_buffer_t *buffer) const { + unsigned int table_index = 1; + unsigned int i = 0; + + for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) { + const pause_map_t *pause = &pauses[table_index][pause_index]; + for (; i < pause->num_lookups; i++) + hb_ot_layout_position_lookup (font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask); + + pause->callback.func.gpos (this, font, buffer, pause->callback.user_data); + } + + for (; i < lookups[table_index].len; i++) + hb_ot_layout_position_lookup (font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask); +} + + +/* TODO refactor the following two functions */ + +void hb_ot_map_builder_t::add_gsub_pause (hb_ot_map_t::gsub_pause_func_t pause_func, void *user_data) +{ + unsigned int table_index = 0; + + if (pause_func) { + pause_info_t *p = pauses[table_index].push (); + if (likely (p)) { + p->stage = current_stage[table_index]; + p->callback.func.gsub = pause_func; + p->callback.user_data = user_data; + } + } + + current_stage[table_index]++; +} + +void hb_ot_map_builder_t::add_gpos_pause (hb_ot_map_t::gpos_pause_func_t pause_func, void *user_data) +{ + unsigned int table_index = 1; + + if (pause_func) { + pause_info_t *p = pauses[table_index].push (); + if (likely (p)) { + p->stage = current_stage[table_index]; + p->callback.func.gpos = pause_func; + p->callback.user_data = user_data; + } + } + + current_stage[table_index]++; } void @@ -111,13 +181,17 @@ hb_ot_map_builder_t::compile (hb_face_t *face, if (feature_infos[i].tag != feature_infos[j].tag) feature_infos[++j] = feature_infos[i]; else { - if (feature_infos[i].global) - feature_infos[j] = feature_infos[i]; - else { + if (feature_infos[i].global) { + feature_infos[j].global = true; + feature_infos[j].max_value = feature_infos[i].max_value; + feature_infos[j].default_value = feature_infos[i].default_value; + } else { feature_infos[j].global = false; feature_infos[j].max_value = MAX (feature_infos[j].max_value, feature_infos[i].max_value); - /* Inherit default_value from j */ } + feature_infos[j].stage[0] = MIN (feature_infos[j].stage[0], feature_infos[i].stage[0]); + feature_infos[j].stage[1] = MIN (feature_infos[j].stage[1], feature_infos[i].stage[1]); + /* Inherit default_value from j */ } feature_infos.shrink (j + 1); } @@ -160,6 +234,8 @@ hb_ot_map_builder_t::compile (hb_face_t *face, map->tag = info->tag; map->index[0] = feature_index[0]; map->index[1] = feature_index[1]; + map->stage[0] = info->stage[0]; + map->stage[1] = info->stage[1]; if (info->global && info->max_value == 1) { /* Uses the global bit */ map->shift = 0; @@ -177,6 +253,9 @@ hb_ot_map_builder_t::compile (hb_face_t *face, feature_infos.shrink (0); /* Done with these */ + add_gsub_pause (NULL, NULL); + add_gpos_pause (NULL, NULL); + for (unsigned int table_index = 0; table_index < 2; table_index++) { hb_tag_t table_tag = table_tags[table_index]; @@ -190,20 +269,39 @@ hb_ot_map_builder_t::compile (hb_face_t *face, &required_feature_index)) m.add_lookups (face, table_index, required_feature_index, 1); - for (unsigned i = 0; i < m.features.len; i++) - m.add_lookups (face, table_index, m.features[i].index[table_index], m.features[i].mask); - - /* Sort lookups and merge duplicates */ - m.lookups[table_index].sort (); - if (m.lookups[table_index].len) + unsigned int pause_index = 0; + unsigned int last_num_lookups = 0; + for (unsigned stage = 0; stage < current_stage[table_index]; stage++) { - unsigned int j = 0; - for (unsigned int i = 1; i < m.lookups[table_index].len; i++) - if (m.lookups[table_index][i].index != m.lookups[table_index][j].index) - m.lookups[table_index][++j] = m.lookups[table_index][i]; - else - m.lookups[table_index][j].mask |= m.lookups[table_index][i].mask; - m.lookups[table_index].shrink (j + 1); + for (unsigned i = 0; i < m.features.len; i++) + if (m.features[i].stage[table_index] == stage) + m.add_lookups (face, table_index, m.features[i].index[table_index], m.features[i].mask); + + /* Sort lookups and merge duplicates */ + if (last_num_lookups < m.lookups[table_index].len) + { + m.lookups[table_index].sort (last_num_lookups, m.lookups[table_index].len); + + unsigned int j = last_num_lookups; + for (unsigned int i = j + 1; i < m.lookups[table_index].len; i++) + if (m.lookups[table_index][i].index != m.lookups[table_index][j].index) + m.lookups[table_index][++j] = m.lookups[table_index][i]; + else + m.lookups[table_index][j].mask |= m.lookups[table_index][i].mask; + m.lookups[table_index].shrink (j + 1); + } + + last_num_lookups = m.lookups[table_index].len; + + if (pause_index < pauses[table_index].len && pauses[table_index][pause_index].stage == stage) { + hb_ot_map_t::pause_map_t *pause_map = m.pauses[table_index].push (); + if (likely (pause_map)) { + pause_map->num_lookups = last_num_lookups; + pause_map->callback = pauses[table_index][pause_index].callback; + } + + pause_index++; + } } } } diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc index edcd7fb62f10d18e041358f954636ac04ccc32ed..4cbdde12e859f428043df52a467e5a65111558aa 100644 --- a/src/hb-ot-shape-complex-arabic.cc +++ b/src/hb-ot-shape-complex-arabic.cc @@ -153,12 +153,32 @@ static const struct arabic_state_table_entry { void _hb_ot_shape_complex_collect_features_arabic (hb_ot_shape_planner_t *planner, const hb_segment_properties_t *props) { - /* ArabicOT spec enables 'cswh' for Arabic where as for basic shaper it's disabled by default. */ - planner->map.add_bool_feature (HB_TAG('c','s','w','h')); + /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together, + * then rlig and calt each in their own stage. This makes IranNastaliq's ALLAH + * ligature work correctly. It's unfortunate though... + * + * This also makes Arial Bold in Windows7 work. See: + * https://bugzilla.mozilla.org/show_bug.cgi?id=644184 + * + * TODO: Add test cases for these two. + */ + + planner->map.add_gsub_pause (NULL, NULL); unsigned int num_features = props->script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES; for (unsigned int i = 0; i < num_features; i++) planner->map.add_bool_feature (arabic_syriac_features[i], false); + + planner->map.add_gsub_pause (NULL, NULL); + + planner->map.add_bool_feature (HB_TAG('r','l','i','g')); + planner->map.add_gsub_pause (NULL, NULL); + + planner->map.add_bool_feature (HB_TAG('c','a','l','t')); + planner->map.add_gsub_pause (NULL, NULL); + + /* ArabicOT spec enables 'cswh' for Arabic where as for basic shaper it's disabled by default. */ + planner->map.add_bool_feature (HB_TAG('c','s','w','h')); } void diff --git a/src/hb-private.hh b/src/hb-private.hh index d749267abb7f5fc08d4c51a788e2f837d46945df..1cf11bcb7b2576c9e2909539c3e9e439dd072891 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -304,6 +304,11 @@ struct hb_prealloced_array_t { qsort (array, len, sizeof (Type), (hb_compare_func_t) Type::cmp); } + inline void sort (unsigned int start, unsigned int end) + { + qsort (array + start, end - start, sizeof (Type), (hb_compare_func_t) Type::cmp); + } + template inline Type *bsearch (T *key) {