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 {
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 */
{ 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,
hb_prealloced_array_t<feature_map_t, 8> features;
hb_prealloced_array_t<lookup_map_t, 32> lookups[2]; /* GSUB/GPOS */
+ hb_prealloced_array_t<pause_map_t, 1> pauses[2]; /* GSUB/GPOS */
};
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:
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_info_t,16> 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_info_t,16> feature_infos;
+ hb_prealloced_array_t<pause_info_t, 1> pauses[2]; /* GSUB/GPOS */
};
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
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);
}
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;
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];
&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++;
+ }
}
}
}
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