/* Pango * pango-ot-ruleset.c: Shaping using OpenType features * * Copyright (C) 2000 Red Hat Software * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "pango-ot-private.h" #define PANGO_SCALE_26_6 (PANGO_SCALE / (1<<6)) #define PANGO_UNITS_26_6(d) (PANGO_SCALE_26_6 * (d)) typedef struct _PangoOTRule PangoOTRule; struct _PangoOTRule { gulong property_bit; FT_UShort feature_index; guint table_type : 1; }; static void pango_ot_ruleset_class_init (GObjectClass *object_class); static void pango_ot_ruleset_init (PangoOTRuleset *ruleset); static void pango_ot_ruleset_finalize (GObject *object); static GObjectClass *parent_class; GType pango_ot_ruleset_get_type (void) { static GType object_type = 0; if (!object_type) { static const GTypeInfo object_info = { sizeof (PangoOTRulesetClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc)pango_ot_ruleset_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (PangoOTRuleset), 0, /* n_preallocs */ (GInstanceInitFunc)pango_ot_ruleset_init, }; object_type = g_type_register_static (G_TYPE_OBJECT, "PangoOTRuleset", &object_info, 0); } return object_type; } static void pango_ot_ruleset_class_init (GObjectClass *object_class) { parent_class = g_type_class_peek_parent (object_class); object_class->finalize = pango_ot_ruleset_finalize; } static void pango_ot_ruleset_init (PangoOTRuleset *ruleset) { ruleset->rules = g_array_new (FALSE, FALSE, sizeof (PangoOTRule)); } static void pango_ot_ruleset_finalize (GObject *object) { PangoOTRuleset *ruleset = PANGO_OT_RULESET (object); g_array_free (ruleset->rules, TRUE); g_object_unref (G_OBJECT (ruleset->info)); } /** * pango_ot_ruleset_new: * @info: a #PangoOTInfo. * @returns: a new #PangoOTRuleset. * * Creates a new #PangoOTRuleset for the given OpenType info. **/ PangoOTRuleset * pango_ot_ruleset_new (PangoOTInfo *info) { PangoOTRuleset *ruleset; ruleset = g_object_new (PANGO_TYPE_OT_RULESET, NULL); ruleset->info = g_object_ref (G_OBJECT (info)); return ruleset; } /** * pango_ot_ruleset_add_feature: * @ruleset: a #PangoOTRuleset. * @table_type: the table type to add a feature to. * @feature_index: the index of the feature to add. * @property_bit: the property bit to use for this feature. * * Adds a feature to the ruleset. See pango_ot_ruleset_shape() * for an explanation of @property_bit. **/ void pango_ot_ruleset_add_feature (PangoOTRuleset *ruleset, PangoOTTableType table_type, guint feature_index, gulong property_bit) { PangoOTRule tmp_rule; g_return_if_fail (PANGO_OT_IS_RULESET (ruleset)); tmp_rule.table_type = table_type; tmp_rule.feature_index = feature_index; tmp_rule.property_bit = property_bit; g_array_append_val (ruleset->rules, tmp_rule); } /** * pango_ot_ruleset_shape: * @ruleset: a #PangoOTRuleset. * @glyphs: a pointer to a #PangoGlyphString. * @properties: an array containing one #gulong bitfield for each glyph, * which gives the glyph's properties: If a certain bit is set for a glyph, * the feature which has the same bit set in its property value is applied. * * Shapes a string of glyphs with the given properties according to @ruleset. **/ void pango_ot_ruleset_shape (PangoOTRuleset *ruleset, PangoGlyphString *glyphs, gulong *properties) { int i; int last_cluster; TTO_GSUB gsub = NULL; TTO_GPOS gpos = NULL; TTO_GSUB_String *in_string = NULL; TTO_GSUB_String *out_string = NULL; TTO_GSUB_String *result_string = NULL; gboolean need_gsub = FALSE; gboolean need_gpos = FALSE; g_return_if_fail (PANGO_OT_IS_RULESET (ruleset)); for (i = 0; i < ruleset->rules->len; i++) { PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i); if (rule->table_type == PANGO_OT_TABLE_GSUB) need_gsub = TRUE; else need_gpos = TRUE; } if (need_gsub) { gsub = pango_ot_info_get_gsub (ruleset->info); if (gsub) TT_GSUB_Clear_Features (gsub); } if (need_gpos) { gpos = pango_ot_info_get_gpos (ruleset->info); if (gpos) TT_GPOS_Clear_Features (gpos); } for (i = 0; i < ruleset->rules->len; i++) { PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i); if (rule->table_type == PANGO_OT_TABLE_GSUB) { if (gsub) TT_GSUB_Add_Feature (gsub, rule->feature_index, rule->property_bit); } else { if (gpos) TT_GPOS_Add_Feature (gpos, rule->feature_index, rule->property_bit); } } if (!gsub && !gpos) return; g_assert (TT_GSUB_String_New (ruleset->info->face->memory, &in_string) == FT_Err_Ok); g_assert (TT_GSUB_String_Set_Length (in_string, glyphs->num_glyphs) == FT_Err_Ok); for (i = 0; i < glyphs->num_glyphs; i++) { in_string->string[i] = glyphs->glyphs[i].glyph; in_string->properties[i] = properties[i]; in_string->logClusters[i] = glyphs->log_clusters[i]; } in_string->max_ligID = i; if (gsub) { g_assert (TT_GSUB_String_New (ruleset->info->face->memory, &out_string) == FT_Err_Ok); result_string = out_string; TT_GSUB_Apply_String (gsub, in_string, out_string); } else result_string = in_string; if (gpos) { TTO_GPOS_Data *outgpos = NULL; if (!TT_GPOS_Apply_String (ruleset->info->face, gpos, 0, result_string, &outgpos, FALSE /* enable device-dependant values */, FALSE /* Even though this might be r2l text, RTL is handled elsewhere */)) { for (i = 0; i < result_string->length; i++) { int j; glyphs->glyphs[i].geometry.x_offset += PANGO_UNITS_26_6 (outgpos[i].x_pos); glyphs->glyphs[i].geometry.y_offset += PANGO_UNITS_26_6 (outgpos[i].y_pos); for (j = i - outgpos[i].back; j < i; j++) glyphs->glyphs[i].geometry.x_offset -= glyphs->glyphs[j].geometry.width; if (outgpos[i].new_advance) /* Can't set new x offset for marks, so just make sure not to increase it. Can do better than this by playing with ->x_offset. */ glyphs->glyphs[i].geometry.width = 0; else glyphs->glyphs[i].geometry.width += PANGO_UNITS_26_6(outgpos[i].x_advance); } FT_Free(gpos->memory, (void *)outgpos); } } pango_glyph_string_set_size (glyphs, result_string->length); last_cluster = -1; for (i = 0; i < result_string->length; i++) { glyphs->glyphs[i].glyph = result_string->string[i]; glyphs->glyphs[i].glyph = result_string->string[i]; glyphs->log_clusters[i] = result_string->logClusters[i]; if (glyphs->log_clusters[i] != last_cluster) glyphs->glyphs[i].attr.is_cluster_start = 1; else glyphs->glyphs[i].attr.is_cluster_start = 0; last_cluster = glyphs->log_clusters[i]; } if (in_string) TT_GSUB_String_Done (in_string); if (out_string) TT_GSUB_String_Done (out_string); }