| /* |
| * 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 "ot-ruleset.h" |
| #include "ot-info.h" |
| |
| #define noRULESET_DEBUG_MEMORY |
| |
| #include FT_INTERNAL_MEMORY_H /* For FT_Free() */ |
| |
| #define OT_SCALE_26_6 (OT_SCALE / (1<<6)) |
| #define OT_UNITS_26_6(d) (OT_SCALE_26_6 * (d)) |
| |
| typedef struct _OTRule OTRule; |
| |
| struct _OTRule { |
| FT_ULong property_bit; |
| FT_UShort feature_index; |
| FT_UInt table_type : 1; |
| }; |
| |
| OTRuleset * |
| ot_ruleset_new (OTInfo *info) |
| { |
| OTRuleset *ruleset; |
| |
| ruleset = malloc (sizeof (OTRuleset)); |
| |
| ot_ruleset_setup (ruleset, info); |
| |
| return ruleset; |
| } |
| |
| OTRuleset * |
| ot_ruleset_delete (OTRuleset *ruleset) |
| { |
| if (ruleset) { |
| ot_ruleset_release (ruleset); |
| free (ruleset); |
| } |
| |
| return NULL; |
| } |
| |
| #ifdef RULESET_DEBUG_MEMORY |
| static ot_ruleset_count = 0; |
| #endif |
| |
| OTRuleset * |
| ot_ruleset_setup (OTRuleset *ruleset, OTInfo *info) |
| { |
| FT_Memory memory = FT_FACE_MEMORY( info->face ); |
| FT_ASSERT (ruleset != NULL); |
| |
| #ifdef RULESET_DEBUG_MEMORY |
| ot_ruleset_count++; |
| FT_Message("ot_ruleset_setup: %d\n", ot_ruleset_count); |
| #endif |
| ruleset->info = info; |
| |
| ruleset->rules = OT_Array_New ( sizeof (OTRule), memory ); |
| |
| return ruleset; |
| } |
| |
| OTRuleset * |
| ot_ruleset_release (OTRuleset *ruleset) |
| { |
| #ifdef RULESET_DEBUG_MEMORY |
| ot_ruleset_count--; |
| FT_Message("ot_ruleset_release: %d\n", ot_ruleset_count); |
| #endif |
| if (ruleset->rules) { |
| OT_Array_Free ( ruleset->rules ); |
| ruleset->rules = NULL; |
| } |
| |
| if (ruleset->info) { |
| ruleset->info = NULL; |
| } |
| |
| return NULL; |
| } |
| |
| void |
| ot_ruleset_copy ( OTRuleset * from, OTRuleset * to ) |
| { |
| OTArray *from_array; |
| OTArray *to_array; |
| FT_UInt from_length; |
| FT_UInt i; |
| |
| to->info = from->info; |
| from_array = from->rules; |
| from_length = from_array->length; |
| to_array = to->rules; |
| OT_Array_Set_Size( to_array, from_length ); |
| for ( i = 0; i < from_length; i++ ) |
| OT_Array_Index(to_array, OTRule, i) = OT_Array_Index(from_array, OTRule, i); |
| } |
| |
| /** |
| * ot_ruleset_add_feature: |
| * @ruleset: a #OTRuleset. |
| * @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 ot_ruleset_shape() |
| * for an explanation of @property_bit. |
| **/ |
| void |
| ot_ruleset_add_feature (OTRuleset *ruleset, OTTableType table_type, FT_UInt feature_index, FT_ULong property_bit) |
| { |
| OTRule tmp_rule; |
| |
| FT_ASSERT (ruleset != NULL); |
| |
| tmp_rule.table_type = table_type; |
| tmp_rule.feature_index = feature_index; |
| tmp_rule.property_bit = property_bit; |
| |
| OT_Array_Append_Val (ruleset->rules, &tmp_rule); |
| } |
| |
| /** |
| * ot_ruleset_shape: |
| * @ruleset: a #OTRuleset. |
| * @glyphs: a pointer to a #OTGlyphString. |
| * |
| * Shapes a string of glyphs with the given properties according to @ruleset. |
| **/ |
| OTGlyphString * |
| ot_ruleset_shape ( OTRuleset *ruleset, OTGlyphString *glyphs ) |
| { |
| FT_Error error; |
| int i; |
| int last_cluster; |
| int result; |
| |
| 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; |
| |
| FT_Bool need_gsub = 0; |
| FT_Bool need_gpos = 0; |
| |
| FT_ASSERT (ruleset != NULL); |
| |
| for (i = 0; i < ruleset->rules->length; i++) |
| { |
| OTRule *rule = &OT_Array_Index (ruleset->rules, OTRule, i); |
| |
| if (rule->table_type == OT_TABLE_GSUB) |
| need_gsub = 1; |
| else |
| need_gpos = 1; |
| } |
| |
| if (need_gsub) |
| { |
| gsub = ot_info_get_gsub (ruleset->info); |
| |
| if (gsub) |
| TT_GSUB_Clear_Features (gsub); |
| } |
| |
| if (need_gpos) |
| { |
| gpos = ot_info_get_gpos (ruleset->info); |
| |
| if (gpos) |
| TT_GPOS_Clear_Features (gpos); |
| } |
| |
| for (i = 0; i < ruleset->rules->length; i++) |
| { |
| OTRule *rule = &OT_Array_Index (ruleset->rules, OTRule, i); |
| |
| if (rule->table_type == 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 glyphs; |
| |
| result = TT_GSUB_String_New (ruleset->info->face->memory, &in_string); |
| FT_ASSERT (result == FT_Err_Ok); |
| |
| result = TT_GSUB_String_Set_Length (in_string, glyphs->length); |
| FT_ASSERT (result == FT_Err_Ok); |
| |
| for (i = 0; i < glyphs->length; i++) |
| { |
| in_string->string[i] = glyphs->glyphs[i].gid; |
| in_string->properties[i] = glyphs->glyphs[i].ot_prop; |
| #if 0 |
| in_string->logClusters[i] = glyphs->log_clusters[i]; |
| #endif |
| } |
| in_string->max_ligID = i; |
| |
| if (gsub) |
| { |
| result = TT_GSUB_String_New (ruleset->info->face->memory, |
| &out_string); |
| FT_ASSERT (result == FT_Err_Ok); |
| result_string = out_string; |
| |
| TT_GSUB_Apply_String (gsub, in_string, out_string); |
| } |
| else |
| result_string = in_string; |
| |
| #if 0 /* TODO: implement this using nr-glyphs */ |
| 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++) |
| { |
| FT_Pos x_pos = outgpos[i].x_pos; |
| FT_Pos y_pos = outgpos[i].y_pos; |
| int back = i; |
| int j; |
| |
| while (outgpos[back].back != 0) |
| { |
| back -= outgpos[back].back; |
| x_pos += outgpos[back].x_pos; |
| y_pos += outgpos[back].y_pos; |
| } |
| |
| for (j = back; j < i; j++) |
| glyphs->glyphs[i].geometry.x_offset -= glyphs->glyphs[j].geometry.width; |
| |
| glyphs->glyphs[i].geometry.x_offset += OT_UNITS_26_6(x_pos); |
| glyphs->glyphs[i].geometry.y_offset += OT_UNITS_26_6(y_pos); |
| |
| if (outgpos[i].new_advance) |
| glyphs->glyphs[i].geometry.width = OT_UNITS_26_6(outgpos[i].x_advance); |
| else |
| glyphs->glyphs[i].geometry.width += OT_UNITS_26_6(outgpos[i].x_advance); |
| } |
| |
| FT_Free(gpos->memory, (void *)outgpos); |
| } |
| } |
| #endif |
| |
| if (glyphs->length != result_string->length) |
| { |
| if (( error = FTL_Set_Glyphs_Array_Length( glyphs, result_string->length ) )) |
| return glyphs; |
| } |
| |
| last_cluster = -1; |
| for (i = 0; i < result_string->length; i++) |
| { |
| glyphs->glyphs[i].gid = result_string->string[i]; |
| /* TODO: Other fields */ |
| #if 0 /* TODO: */ |
| 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]; |
| #endif |
| } |
| |
| if (in_string) |
| TT_GSUB_String_Done (in_string); |
| if (out_string) |
| TT_GSUB_String_Done (out_string); |
| |
| return glyphs; |
| } |