/***************************************************************************/ /* */ /* gxdemo.c */ /* */ /* Demo program for AAT/TrueTypeGX font driver implementation (body). */ /* */ /* Copyright 2003 by */ /* Masatake YAMATO and Redhat K.K. */ /* */ /* This file may only be used, */ /* modified, and distributed under the terms of the FreeType project */ /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ /* this file you indicate that you have read the license and */ /* understand and accept it fully. */ /* */ /***************************************************************************/ /***************************************************************************/ /* Development of the code in this file is support of */ /* Information-technology Promotion Agency, Japan. */ /***************************************************************************/ #define _XOPEN_SOURCE #include #include #include FT_GXLAYOUT_H #include FT_BBOX_H #include FT_OUTLINE_H #include FT_INTERNAL_DEBUG_H #include #include #include #include #include #include #include #include #include "gxdump.h" #include "gxfeatreg.h" #include "gxtypes.h" #include "gxload.h" #include "gxaccess.h" #define DEFAULT_UNIT 1024 #define BUFFER_LENGTH 1024 static char buffer[BUFFER_LENGTH]; static GHashTable * setting_buttons = NULL; static gulong dump_flags = GX_DUMP_mort|GX_DUMP_morx|GX_DUMP_feat|GX_DUMP_kern; static gboolean dump_glyph_metrics = FALSE; static GtkAdjustment * gid_spinner_adj; static GtkWidget *glyph_canvas; static GnomeCanvasItem *root_rect_item = NULL; static GnomeCanvasItem *bbox_item = NULL; static GnomeCanvasItem *h_advance_item = NULL; static GnomeCanvasItem *v_advance_item = NULL; static GnomeCanvasItem *pixbuf_item = NULL; static GSList *div_items = NULL; static int default_gid = 19; void create_window ( GX_Face face ); void destroy_window ( GtkObject * unused, GXL_FeaturesRequest request ); void radio_toggled( GtkToggleButton * toggle, gpointer setting ); void check_toggled( GtkToggleButton * toggle, gpointer setting); void run_layout_engine ( GtkButton * button, gpointer request ); void reset_feature_request( GtkButton * button, gpointer request ); void check_table ( GtkToggleButton * toggle_button, gpointer flag); void dump_feature_request( GtkButton * button, gpointer request ); void dump_feature_registry( GtkButton * button, gpointer request ); void dump_language_id ( GtkButton * button, gpointer face ); void horizontal_radio_toggled( GtkToggleButton * toggle, gpointer request ); void vertical_radio_toggled( GtkToggleButton * toggle, gpointer request ); void dump_file(FT_Library library, const char * file, gint verbose); void dump_face(FT_Face face, const char* file, gint verbose); void dump_glyph(FT_Face face, FT_UShort gid, FTL_Direction direction); void activate_chain_trace( void ); void set_dump_glyph_metrics ( GtkWidget * check_button, gpointer data ); void render_glyph ( GtkWidget * button, gpointer request ); void set_trace_level( GtkAdjustment * adj, gpointer trace ); #define DUMP_DESC "Supported tables are mort,morx,feat,prop,trak,kern,just,lcar,opbd,bsln,fmtx,fdsc" static const GDebugKey dump_keys[] = { {"mort", GX_DUMP_mort}, {"morx", GX_DUMP_morx}, {"feat", GX_DUMP_feat}, {"prop", GX_DUMP_prop}, {"trak", GX_DUMP_trak}, {"kern", GX_DUMP_kern}, {"just", GX_DUMP_just}, {"lcar", GX_DUMP_lcar}, {"opbd", GX_DUMP_opbd}, {"bsln", GX_DUMP_bsln}, {"fmtx", GX_DUMP_fmtx}, {"fdsc", GX_DUMP_fdsc}, }; int main(int argc, char ** argv) { FT_Library library; FT_Face face; poptContext optCon; int rc; const char * file; static char* arg_debug = NULL; static int arg_batch = 0; static int arg_memprof = 0; static int arg_verbose = 0; static int arg_trace_chain = 0; const int ndump_keys = sizeof(dump_keys)/sizeof(GDebugKey); struct poptOption optTable [] = { { "trace-chain", '\0', POPT_ARG_NONE, &arg_trace_chain, 0, "Dump chains selection", ""}, { "default-gid", '\0', POPT_ARG_INT, &default_gid, 0, "Default GID", ""}, { "batch", 'b', POPT_ARG_NONE, &arg_batch, 0, "batch mode", ""}, { "table", 't', POPT_ARG_STRING, &arg_debug, 0, DUMP_DESC, "tableName"}, { "memprof", 'm', POPT_ARG_NONE, &arg_memprof, 0, "Enter to infinite loop to run under memprof", ""}, { "verbose", 'v', POPT_ARG_NONE, &arg_verbose, 0, "Print extra infomation to stdout", ""}, POPT_AUTOHELP POPT_TABLEEND }; FTL_EngineType engine_type; gtk_init(&argc, &argv); optCon = poptGetContext(argv[0], argc, (const char **)argv, optTable, 0); poptSetOtherOptionHelp(optCon, "[options] gxfont\n"); rc = poptReadDefaultConfig (optCon, 0); if (rc < 0) { fprintf(stderr, "Fail to read .popt config file: %s\n", poptStrerror(rc)); exit (1); } while ((rc = poptGetNextOpt(optCon)) > 0) if (rc != -1) { fprintf(stderr, "Bad argument %s: %s\n", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(rc)); exit (1); } if (arg_trace_chain) activate_chain_trace(); if (FT_Init_FreeType (&library)) { fprintf(stderr, "Error in %s\n", "FT_Init_FreeType"); exit (1); } if (arg_debug) dump_flags = g_parse_debug_string (arg_debug, (GDebugKey *) dump_keys, ndump_keys); file = poptGetArg(optCon); if (!file) { poptPrintHelp(optCon, stderr, 0); exit(1); } if ( arg_batch ) { fprintf(stdout, "\n"); do { dump_file( library, file, arg_verbose ); file = poptGetArg(optCon); } while (file); fprintf(stdout, "\n"); goto Exit; } if ( FT_New_Face (library, file, 0, &face) ) { fprintf(stderr, "Error in %s: %s\n", "FT_New_Face", file); exit (1); } #if 0 if ( FT_HAS_VERTICAL(face) ) fprintf(stdout, "Face has vertical infomation\n"); else fprintf(stdout, "Face does not have vertical infomation\n"); #endif /* 0 */ if (( FTL_Query_EngineType( face, &engine_type ) ) || ( engine_type != FTL_TRUETYPEGX_ENGINE )) { fprintf(stderr, "No GX table is existed: %s\n", file); exit ( 1 ); } setting_buttons = g_hash_table_new(NULL, NULL); create_window( (GX_Face)face ); if ( FT_Done_Face ( face ) ) fprintf(stderr, "Error in %s: %s\n", "FT_Done_Face", file); Exit: if ( FT_Done_FreeType (library) ) { fprintf(stderr, "Error in %s\n", "FT_Done_FreeType"); exit(1); } if ( arg_memprof || getenv("_MEMPROF_SOCKET") ) { fprintf(stderr, "Enter infinite loop for memprof\n"); while (1); } return 0; } GtkWidget * create_gx_window( GX_Face face ); GtkWidget * create_feat_area( GXL_FeaturesRequest request ); GtkWidget * create_dump_area( GX_Face face, GXL_FeaturesRequest request ); GtkWidget * create_trace_area(void); void create_window (GX_Face face) { GtkWidget * window; window = create_gx_window ( face ); gtk_widget_show(window); gtk_window_resize( GTK_WINDOW(window), 1, 400); gtk_window_set_title (GTK_WINDOW( window ), ((FT_Face)face)->family_name); gtk_main(); } GtkWidget * create_gx_window( GX_Face face ) { GtkWidget * window; GtkWidget * feat; GtkWidget * dump; GtkWidget * trace; GtkWidget * vbox; GtkWidget * hbox; GtkWidget * button; GtkWidget * notebook; GtkWidget * label; GXL_FeaturesRequest request; GtkWidget *gid_spinner; FTL_New_FeaturesRequest ( (FT_Face)face, (FTL_FeaturesRequest*)&request ); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK ( destroy_window ), request); gtk_container_set_border_width (GTK_CONTAINER (window), 4); notebook = gtk_notebook_new (); gtk_widget_show( notebook ); gtk_container_add ( GTK_CONTAINER( window ), notebook ); /* Features */ vbox = gtk_vbox_new ( FALSE, 8 ); label = gtk_label_new("Features"); gtk_notebook_append_page ( GTK_NOTEBOOK(notebook), vbox, label ); gtk_widget_show( vbox ); feat = create_feat_area ( request ); gtk_box_pack_start ( GTK_BOX ( vbox ), feat, TRUE, TRUE, 4 ); hbox = gtk_hbox_new ( TRUE, 4 ); gtk_box_pack_start ( GTK_BOX ( vbox ), hbox, FALSE, TRUE, 4 ); gtk_widget_show( hbox ); button = gtk_button_new_with_label ("Reset"); gtk_widget_show ( button ); g_signal_connect ( G_OBJECT( button ), "clicked", G_CALLBACK ( reset_feature_request ), request ); gtk_box_pack_start ( GTK_BOX ( hbox ), button, TRUE, TRUE, 4 ); button = gtk_button_new_with_label ("Run"); gtk_widget_show ( button ); g_signal_connect ( G_OBJECT( button ), "clicked", G_CALLBACK ( run_layout_engine ), request ); gtk_box_pack_start ( GTK_BOX ( hbox ), button, TRUE, TRUE, 4 ); /* Glyph */ vbox = gtk_vbox_new ( FALSE, 8 ); label = gtk_label_new("Glyph"); gtk_notebook_append_page ( GTK_NOTEBOOK(notebook), vbox, label ); gtk_widget_show ( vbox ); hbox = gtk_hbox_new ( TRUE, 4 ); gtk_box_pack_start ( GTK_BOX ( vbox ), hbox, FALSE, TRUE, 0 ); gtk_widget_show ( hbox ); gid_spinner_adj = (GtkAdjustment *) gtk_adjustment_new ((gdouble)default_gid, 0.0, (gdouble)0xFFFF, 1.0, 5.0, 5.0); gid_spinner = gtk_spin_button_new (gid_spinner_adj, 1.0, 0); gtk_box_pack_start ( GTK_BOX ( hbox ), gid_spinner, FALSE, TRUE, 0 ); gtk_widget_show(gid_spinner); button = gtk_button_new_with_label("Render"); gtk_box_pack_start ( GTK_BOX ( hbox ), button, FALSE, TRUE, 0 ); g_signal_connect( button, "clicked", G_CALLBACK( render_glyph ), request ); gtk_widget_push_visual (gdk_rgb_get_visual ()); gtk_widget_push_colormap (gdk_rgb_get_cmap ()); glyph_canvas = gnome_canvas_new_aa (); gtk_widget_pop_colormap (); gtk_widget_pop_visual (); gtk_box_pack_start ( GTK_BOX ( vbox ), glyph_canvas, TRUE, TRUE, 4 ); gnome_canvas_set_pixels_per_unit(GNOME_CANVAS(glyph_canvas),0.2); gnome_canvas_set_scroll_region(GNOME_CANVAS(glyph_canvas),0.0,0.0, (double)DEFAULT_UNIT, (double)-DEFAULT_UNIT ); gtk_widget_show( glyph_canvas ); gtk_widget_show ( button ); /* Styles */ vbox = gtk_vbox_new ( FALSE, 8 ); label = gtk_label_new("Styles"); gtk_notebook_append_page ( GTK_NOTEBOOK(notebook), vbox, label ); gtk_widget_show ( vbox ); /* Variations */ vbox = gtk_vbox_new ( FALSE, 8 ); label = gtk_label_new("Variations"); gtk_notebook_append_page ( GTK_NOTEBOOK(notebook), vbox, label ); /* gtk_widget_show ( vbox ); */ /* Dump */ dump = create_dump_area( face, request ); label = gtk_label_new("Dump"); gtk_notebook_append_page ( GTK_NOTEBOOK(notebook), dump, label ); gtk_widget_show ( dump ); /* Trace */ trace = create_trace_area(); label = gtk_label_new("Trace"); gtk_notebook_append_page ( GTK_NOTEBOOK(notebook), trace, label ); gtk_widget_show ( trace ); return window; } GtkWidget * create_feat_area( GXL_FeaturesRequest request ) { GtkWidget * features_vbox; GtkWidget * feature_frame; GtkWidget * settings_vbox; GtkWidget * setting_toggle; GtkWidget * setting_radio; GtkWidget * scrolled; GtkWidget * sep; gint i_feat, j_string, k_setting; gint nFeatures = GXL_FeaturesRequest_Get_Feature_Count ( request ); GXL_Feature feature; FT_SfntName feature_name; gint nSettings; GXL_Setting setting; FT_SfntName setting_name; char * c_string; static GSList * group = NULL; scrolled = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC ); features_vbox = gtk_vbox_new( FALSE, 4 ); gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW( scrolled ), features_vbox ); feature_frame = gtk_frame_new ( "Direction" ); gtk_box_pack_start( GTK_BOX( features_vbox ), feature_frame, FALSE, FALSE, 2 ); settings_vbox = gtk_vbox_new( FALSE, 4 ); gtk_container_add( GTK_CONTAINER( feature_frame ), settings_vbox ); setting_radio = gtk_radio_button_new_with_label( group, "Horizontal" ); gtk_box_pack_start( GTK_BOX(settings_vbox), setting_radio, FALSE, FALSE, 0 ); g_signal_connect ( setting_radio, "toggled", G_CALLBACK(horizontal_radio_toggled), request ); group = gtk_radio_button_get_group( GTK_RADIO_BUTTON (setting_radio) ); setting_radio = gtk_radio_button_new_with_label( group, "Vertical" ); gtk_box_pack_start( GTK_BOX(settings_vbox), setting_radio, FALSE, FALSE, 0 ); g_signal_connect ( setting_radio, "toggled", G_CALLBACK(vertical_radio_toggled), request ); sep = gtk_hseparator_new(); gtk_box_pack_start( GTK_BOX(features_vbox), sep, FALSE, FALSE, 0 ); for ( i_feat = 0; i_feat < nFeatures; i_feat++ ) { group = NULL; feature = GXL_FeaturesRequest_Get_Feature ( request, i_feat ); if ( GXL_Feature_Get_Name ( feature, 0, 0, 0, &feature_name ) ) { fprintf(stderr, "Cannot find name\n"); exit (1); } c_string = g_new(char, feature_name.string_len + 1 ); c_string[feature_name.string_len] = '\0'; for (j_string = 0; j_string < feature_name.string_len; j_string++) c_string[j_string] = feature_name.string[j_string]; feature_frame = gtk_frame_new ( c_string ); g_free(c_string); gtk_box_pack_start( GTK_BOX( features_vbox ), feature_frame, FALSE, FALSE, 2 ); settings_vbox = gtk_vbox_new( FALSE, 4 ); gtk_container_add( GTK_CONTAINER( feature_frame ), settings_vbox ); nSettings = GXL_Feature_Get_Setting_Count( feature ); for ( k_setting = 0; k_setting < nSettings; k_setting++ ) { setting = GXL_Feature_Get_Setting( feature, k_setting ); if ( GXL_Setting_Get_Name ( setting, 0, 0, 0, &setting_name ) ) { fprintf (stderr, "Cannot find setting name\n"); exit (1); } c_string = g_new(char, setting_name.string_len + 1 ); c_string[setting_name.string_len] = '\0'; for (j_string = 0; j_string < setting_name.string_len; j_string++) c_string[j_string] = setting_name.string[j_string]; if ( GXL_Feature_Is_Setting_Exclusive (feature) ) { setting_radio = gtk_radio_button_new_with_label(group, c_string); group = gtk_radio_button_get_group( GTK_RADIO_BUTTON (setting_radio) ); g_free(c_string); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(setting_radio), GXL_Setting_Get_State (setting) ); g_signal_connect ( setting_radio, "toggled", G_CALLBACK(radio_toggled), setting ); gtk_box_pack_start( GTK_BOX(settings_vbox), setting_radio, FALSE, FALSE, 0 ); gtk_container_set_border_width (GTK_CONTAINER (setting_radio), 2); g_hash_table_insert ( setting_buttons, setting_radio, setting ); } else { setting_toggle = gtk_check_button_new_with_label(c_string); g_free(c_string); gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(setting_toggle), GXL_Setting_Get_State (setting) ); g_signal_connect ( setting_toggle, "toggled", G_CALLBACK(check_toggled), setting ); gtk_box_pack_start( GTK_BOX(settings_vbox), setting_toggle, FALSE, FALSE, 0 ); gtk_container_set_border_width (GTK_CONTAINER (setting_toggle), 2); g_hash_table_insert ( setting_buttons, setting_toggle, setting ); } } group = NULL; } gtk_widget_show_all(scrolled); return scrolled; } void dump_face_cb( GtkButton * button, gpointer face ); GtkWidget * create_dump_area( GX_Face face, GXL_FeaturesRequest request ) { GtkWidget * vbox = gtk_vbox_new ( FALSE, 0 ); GtkWidget * button; GtkWidget * frame; GtkWidget * dump_face_vbox; GtkWidget * dump_tables_vbox; GtkWidget * check_button; /* Language ID */ button = gtk_button_new_with_label("Dump Language ID"); gtk_box_pack_start ( GTK_BOX ( vbox ), button, FALSE, TRUE, 0 ); g_signal_connect ( G_OBJECT( button ), "clicked", G_CALLBACK ( dump_language_id ), face ); gtk_widget_show ( button ); /* Feature Request */ button = gtk_button_new_with_label("Dump Feature Request"); gtk_box_pack_start ( GTK_BOX ( vbox ), button, FALSE, TRUE, 0 ); g_signal_connect ( G_OBJECT( button ), "clicked", G_CALLBACK ( dump_feature_request ), request ); gtk_widget_show ( button ); /* Feature Registry */ button = gtk_button_new_with_label("Dump Feature Registry"); gtk_box_pack_start ( GTK_BOX ( vbox ), button, FALSE, TRUE, 0 ); g_signal_connect ( G_OBJECT( button ), "clicked", G_CALLBACK ( dump_feature_registry ), NULL ); gtk_widget_show ( button ); /* Dump Glyph Metrics */ button = gtk_check_button_new_with_label ("Enable to Dump Glyph Metrics"); gtk_box_pack_start ( GTK_BOX ( vbox ), button, FALSE, TRUE, 0 ); g_signal_connect ( G_OBJECT( button ), "toggled", G_CALLBACK ( set_dump_glyph_metrics ), &dump_glyph_metrics ); gtk_widget_show ( button ); /* Dump tables */ frame = gtk_frame_new ( "Dump Tables" ); gtk_box_pack_start( GTK_BOX( vbox ), frame, FALSE, FALSE, 2 ); dump_face_vbox = gtk_vbox_new ( FALSE, 0 ); gtk_container_add( GTK_CONTAINER( frame ), dump_face_vbox ); dump_tables_vbox = gtk_vbox_new ( FALSE, 0 ); gtk_container_add( GTK_CONTAINER( dump_face_vbox ), dump_tables_vbox ); #define MAKE_CHECK_BUTTON(tag) \ check_button = gtk_check_button_new_with_label(#tag); \ gtk_container_add( GTK_CONTAINER( dump_tables_vbox ), check_button ); \ gtk_widget_show(check_button); \ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(check_button), \ ( dump_flags & GX_DUMP_##tag )? TRUE: FALSE ); \ g_signal_connect ( G_OBJECT( check_button ), \ "toggled", \ G_CALLBACK(check_table), \ GINT_TO_POINTER(GX_DUMP_##tag) ) MAKE_CHECK_BUTTON(mort); MAKE_CHECK_BUTTON(morx); MAKE_CHECK_BUTTON(feat); MAKE_CHECK_BUTTON(prop); MAKE_CHECK_BUTTON(trak); MAKE_CHECK_BUTTON(kern); MAKE_CHECK_BUTTON(just); MAKE_CHECK_BUTTON(lcar); MAKE_CHECK_BUTTON(opbd); MAKE_CHECK_BUTTON(bsln); MAKE_CHECK_BUTTON(fmtx); MAKE_CHECK_BUTTON(fdsc); button = gtk_button_new_with_label("Dump Font Tables"); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK ( dump_face_cb ), face); gtk_container_add( GTK_CONTAINER( dump_face_vbox ), button ); gtk_widget_show ( button ); gtk_widget_show ( dump_tables_vbox ); gtk_widget_show ( dump_face_vbox ); gtk_widget_show ( frame ); return vbox; } GtkWidget* create_trace_area(void) { int count = FT_Trace_Get_Count(); int i; GtkWidget * vbox, *hbox; GtkWidget * label; GtkWidget * scrolled; const char * label_string; GtkObject * adj; GtkWidget * spinner; int level; scrolled = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC ); vbox = gtk_vbox_new ( TRUE, 1 ); for ( i = 0; i < count; i++ ) { hbox = gtk_hbox_new( TRUE, 2 ); label_string = FT_Trace_Get_Name( i ); label = gtk_label_new( label_string ); gtk_container_add(GTK_CONTAINER(hbox), label); gtk_widget_show(label); #ifdef FT_DEBUG_LEVEL_TRACE level = (gdouble)ft_trace_levels[i]; #else level = 0; #endif /* FT_DEBUG_LEVEL_TRACE */ adj = gtk_adjustment_new(level, 0.0, (gdouble)7, 1.0, 1.0, 1.0); spinner = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 1.0, 0 ); gtk_spin_button_set_range(GTK_SPIN_BUTTON(spinner), 0.0, 7.0); gtk_box_pack_end( GTK_BOX(hbox), spinner, FALSE, TRUE, 0 ); gtk_widget_show(spinner); g_signal_connect( G_OBJECT(adj), "value_changed", G_CALLBACK(set_trace_level), GINT_TO_POINTER(i)); gtk_container_add( GTK_CONTAINER(vbox), hbox); gtk_widget_show(hbox); } gtk_widget_show(vbox); gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW( scrolled ), vbox ); return scrolled; } void dump_face_cb( GtkButton * button, gpointer face ) { dump_face( face, ((FT_Face)face)->family_name, 1); } void check_table ( GtkToggleButton * toggle_button, gpointer flag) { if ( gtk_toggle_button_get_active( toggle_button ) ) dump_flags |= GPOINTER_TO_INT(flag); else dump_flags &= (~(GPOINTER_TO_INT(flag))); } void destroy_window ( GtkObject * unused, GXL_FeaturesRequest request ) { FTL_Done_FeaturesRequest ( (FTL_FeaturesRequest)request ); gtk_main_quit(); } void radio_toggled( GtkToggleButton * toggle, gpointer setting ) { gboolean state = gtk_toggle_button_get_active(toggle); GXL_Setting_Set_State( setting, state ); } void check_toggled( GtkToggleButton * toggle, gpointer setting ) { gboolean state = gtk_toggle_button_get_active(toggle); GXL_Setting_Set_State( setting, state ); } void run_layout_engine ( GtkButton * button, gpointer request ) { FTL_GlyphArray in, out; FT_Face face = ((FTL_FeaturesRequest)request)->font->face; FT_Memory memory = face->driver->root.memory; char * tmp, *next; long value, length = 0, i; printf( "Input: "); tmp = fgets(buffer, BUFFER_LENGTH, stdin); if ( !tmp ) { fprintf(stderr, "Fail to read string\n"); return; } FTL_New_Glyphs_Array ( memory, &in ); FTL_New_Glyphs_Array ( memory, &out ); tmp = buffer; while ( 1 ) { value = strtol(tmp, &next, 10); if (( value == 0 ) && ( tmp == next )) break; else { length++; tmp = next; } } FTL_Set_Glyphs_Array_Length ( in, length ); tmp = buffer; for ( i = 0; i < length; i++ ) in->glyphs[i].gid = (FT_UShort)strtol(tmp, &tmp, 10); FTL_Activate_FeaturesRequest( request ); FTL_Substitute_Glyphs ( face, in, out ); fprintf(stdout, "Substituted: "); for ( i = 0; i < length; i++ ) fprintf(stdout, "%u%s ", out->glyphs[i].gid, (out->glyphs[i].gid == 0xFFFF)? "": ""); fprintf(stdout, "\n"); if ( dump_glyph_metrics ) { fprintf(stdout, "\nGlyph Metrics\n"); fprintf(stdout, "---------------\n"); for ( i = 0; i < length; i++ ) dump_glyph(face, out->glyphs[i].gid, FTL_Get_FeaturesRequest_Direction((FTL_FeaturesRequest)request)); } FTL_Done_Glyphs_Array ( in ); FTL_Done_Glyphs_Array ( out ); } void reflect_request ( gpointer key, gpointer value, gpointer user_data ); void reset_feature_request( GtkButton * button, gpointer request ) { FTL_Reset_FeaturesRequest( request ); g_hash_table_foreach(setting_buttons, reflect_request, NULL); } void reflect_request ( gpointer key, gpointer value, gpointer user_data ) { GtkWidget * button = GTK_WIDGET(key); GXL_Setting setting = value; gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(button), GXL_Setting_Get_State (setting) ); } void dump_feature_request( GtkButton * button, gpointer request ) { gxl_features_request_dump(request, stdout); } void dump_feature_registry( GtkButton * button, gpointer request ) { fprintf(stdout, "Contents of registry data base\n"); fprintf(stdout, "------------------------------\n"); gx_feature_registory_dump(stdout); } void horizontal_radio_toggled( GtkToggleButton * toggle, gpointer request ) { if ( gtk_toggle_button_get_active( toggle ) ) FTL_Set_FeaturesRequest_Direction ( request, FTL_HORIZONTAL ); } void vertical_radio_toggled( GtkToggleButton * toggle, gpointer request ) { if ( gtk_toggle_button_get_active( toggle ) ) FTL_Set_FeaturesRequest_Direction ( request, FTL_VERTICAL ); } void dump_face( FT_Face face, const char * file, gint verbose) { FT_Bool gx_p; FTL_EngineType engine_type; gx_p = !((FTL_Query_EngineType (face, &engine_type)) || (engine_type != FTL_TRUETYPEGX_ENGINE)); if ( verbose ) { if ( gx_p ) fprintf(stderr, "ok\n"); else if (((TT_Face)face)->extra.data) fprintf(stderr, "failed(no gx, cff)\n"); else fprintf(stderr, "failed(no gx)\n"); } if ( gx_p ) gx_face_dump(face, dump_flags, file); fflush ( stdout ); } void dump_file( FT_Library library, const char * file, gint verbose) { FT_Face face; if ( FT_New_Face (library, file, 0, &face) ) { fprintf(stderr, "Error in %s: %s\n", "FT_New_Face", file); return; } if ( verbose ) fprintf(stderr, "loading %s...", file); /* dump_language_id( NULL, face ); */ dump_face( face, file, verbose ); if ( FT_Done_Face ( face ) ) fprintf(stderr, "Error in %s: %s\n", "FT_Done_Face", file); } void dump_language_id ( GtkButton * unused, gpointer face ) { /* See ttnameid.h */ FT_CharMap * charmaps = ((FT_Face)face)->charmaps; FT_Int i, num_charmaps = ((FT_Face)face)->num_charmaps; FT_ULong langid; fprintf(stdout, "Laguage ID for the charmaps in the face(see ttnameid.h)\n"); fprintf(stdout, "---------------------------------------\n"); for ( i = 0; i < num_charmaps; i++ ) { langid = FT_Get_CMap_Language_ID ( charmaps[i] ); switch ( i ) { case 0: fprintf(stdout, "Laguage ID for the 1st charmap: %lu\n", langid ); break; case 1: fprintf(stdout, "Laguage ID for the 2nd charmap: %lu\n", langid ); break; case 2: fprintf(stdout, "Laguage ID for the 3rd charmap: %lu\n", langid ); break; default: fprintf(stdout, "Laguage ID for the %dth charmap: %lu\n", i, langid ); } } } void activate_chain_trace( void ) { char * tmp = getenv("FT2_DEBUG"); if ( !tmp ) tmp = g_strdup("FT2_DEBUG=gxchain:6"); else tmp = g_strconcat (tmp, " gxchain:6", NULL); putenv(tmp); } void dump_glyph(FT_Face face, FT_UShort gid, FTL_Direction dir) { FT_Int32 vmask; FT_BBox bbox; FT_UShort i, ligcount, div, new_div; fprintf(stdout, "gid: %u\n", gid); if ( gid == 0xFFFF ) { fprintf(stdout, "\t\n"); return ; } ligcount = FTL_Get_LigatureCaret_Count ( face, gid ); vmask = (dir == FTL_VERTICAL)? FT_LOAD_VERTICAL_LAYOUT: 0; FT_Load_Glyph (face, gid, vmask | FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP); FT_Outline_Get_CBox (&face->glyph->outline, &bbox); if ( ligcount == 0 ) { fprintf(stdout, "\twidth: %g\n\theight: %g\n", (double)bbox.xMax - (double)bbox.xMin, (double)bbox.yMax - (double)bbox.yMin ); fprintf(stdout, "\tbbox: [xmin: %g ymin: %g xmax: %g ymax: %g]\n", (double)bbox.xMin, (double)bbox.yMin, (double)bbox.xMax, (double)bbox.yMax); } else { div = bbox.xMin; for ( i = 0; i < ligcount; i++ ) { new_div = FTL_Get_LigatureCaret_Division( face, gid, i ); fprintf(stdout, "\twidth[%d]: %u\n\theight[%d]: %g\n", i, new_div - div, i, (double)bbox.yMax - (double)bbox.yMin ); fprintf(stdout, "\tbbox[%d]: [xmin: %g ymin: %g xmax: %g ymax: %g]\n", i, (double)div, (double)bbox.yMin, (double)new_div, (double)bbox.yMax); div = new_div; fprintf(stdout, "\t----------------\n"); } fprintf(stdout, "\twidth[%d]: %g\n\theight[%d]: %g\n", i, (double)bbox.xMax - (double)div, i, (double)bbox.yMax - (double)bbox.yMin ); fprintf(stdout, "\tbbox[%d]: [xmin: %g ymin: %g xmax: %g ymax: %g]\n", i, (double)div, (double)bbox.yMin, (double)bbox.xMax, (double)bbox.yMax); } } void set_dump_glyph_metrics ( GtkWidget * check_button, gpointer data ) { *(gboolean*)data = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(check_button) ); } void render_glyph ( GtkWidget * button, gpointer request ) { FT_Error error; GnomeCanvasGroup *root = gnome_canvas_root(GNOME_CANVAS(glyph_canvas)); GnomeCanvasItem *div_item; GdkPixbuf *pixbuf; FT_Face face = ((FTL_FeaturesRequest)request)->font->face; FTL_Direction dir = FTL_Get_FeaturesRequest_Direction((FTL_FeaturesRequest)request); FT_UShort gid = (FT_UShort)gtk_adjustment_get_value(gid_spinner_adj); FT_Int32 vmask; FT_BBox bbox; FT_UShort i, ligcount, div; double affine[6] = {1.0, 0.0, 0.0, -1.0, 0.0, 0.0}; double affine_with_bearing[6] = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0}; double factor = (double)DEFAULT_UNIT/(double)face->units_per_EM; if ( dump_glyph_metrics ) dump_glyph ( face, gid, dir ); if ( pixbuf_item ) { gtk_object_destroy( GTK_OBJECT(pixbuf_item) ); pixbuf_item = NULL; } if ( root_rect_item ) gtk_object_destroy( GTK_OBJECT(root_rect_item) ); if ( bbox_item ) gtk_object_destroy( GTK_OBJECT(bbox_item) ); if ( h_advance_item ) gtk_object_destroy( GTK_OBJECT(h_advance_item) ); if ( v_advance_item ) gtk_object_destroy( GTK_OBJECT(v_advance_item) ); if ( div_items ) { GSList * tmp; for ( tmp = div_items; tmp; tmp = tmp->next ) gtk_object_destroy( tmp->data ); g_slist_free ( div_items ); div_items = NULL; } error = FT_Set_Pixel_Sizes(face, 0, DEFAULT_UNIT ); if ( error ) { fprintf(stderr, "Fail in FT_Set_Pixel_Sizes\n"); return ; } vmask = (dir == FTL_VERTICAL)? FT_LOAD_VERTICAL_LAYOUT: 0; error = FT_Load_Glyph (face, gid, vmask | FT_LOAD_NO_BITMAP); if ( error ) { fprintf(stderr, "Fail in FT_Load_Glyph[0]\n"); return ; } error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL ); if ( error ) { fprintf(stderr, "Fail in FT_Render_Glyph\n"); return ; } #if 0 fprintf(stdout, "gid: %d row: %d, width: %d, pitch: %d\n", gid, face->glyph->bitmap.rows, face->glyph->bitmap.width, face->glyph->bitmap.pitch); #endif /* 0 */ if ( face->glyph->bitmap.width == 0 ) goto IGNORE_PIXBUF; pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, TRUE, 8, face->glyph->bitmap.width, face->glyph->bitmap.rows ); { int x, y; unsigned char src; unsigned char* src_buffer; guchar *dist_buffer = gdk_pixbuf_get_pixels (pixbuf); guchar * dist; int rowstride = gdk_pixbuf_get_rowstride (pixbuf); for ( y = 0; y < face->glyph->bitmap.rows; y++ ) { src_buffer = &(face->glyph->bitmap.buffer[y*face->glyph->bitmap.pitch]); for (x = 0; x < face->glyph->bitmap.width; x++ ) { src = src_buffer[x]; dist = dist_buffer + y * rowstride + x * 4; dist[0] = dist[1] = dist[2] = 255 - src; if (255 - src) dist[3] = 0; else dist[3] = 255; } } } pixbuf_item = gnome_canvas_item_new(root, GNOME_TYPE_CANVAS_PIXBUF, "pixbuf", pixbuf, NULL); g_object_unref(pixbuf); IGNORE_PIXBUF: error = FT_Load_Glyph (face, gid, vmask | FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP); if ( error ) { fprintf(stderr, "Fail in FT_Load_Glyph[1]\n"); return ; } FT_Outline_Get_CBox (&face->glyph->outline, &bbox); if ( error ) { fprintf(stderr, "Fail in FT_Outline_Get_CBox\n"); return ; } root_rect_item = gnome_canvas_item_new(root, GNOME_TYPE_CANVAS_RECT, "x1", (double)0, "y1", (double)0, "x2", (double)DEFAULT_UNIT, "y2", (double)DEFAULT_UNIT, "outline_color", "black", NULL); gnome_canvas_item_affine_relative( root_rect_item, affine ); affine[0] *= factor; affine[3] *= factor; h_advance_item = gnome_canvas_item_new( root, GNOME_TYPE_CANVAS_RECT, "x1", (double)0, "y1", (double)0, "x2", (double)face->glyph->metrics.horiAdvance, "y2", (double)0, "outline_color", "red", NULL); gnome_canvas_item_affine_relative( h_advance_item, affine ); { /* Next line is workaround against libart(?)'s bug */ double vertAdvance = ((face->glyph->metrics.vertAdvance)/2)*2.0; v_advance_item = gnome_canvas_item_new( root, GNOME_TYPE_CANVAS_RECT, "x1", (double)0, "y1", (double)0, "x2", (double)0, "y2", (double)vertAdvance, "outline_color", "red", NULL); } gnome_canvas_item_affine_relative( v_advance_item, affine ); bbox_item = gnome_canvas_item_new(root, GNOME_TYPE_CANVAS_RECT, "x1", (double)bbox.xMin, "y1", (double)bbox.yMin, "x2", (double)bbox.xMax, "y2", (double)bbox.yMax, "outline_color", "blue", NULL); gnome_canvas_item_affine_relative( bbox_item, affine ); ligcount = FTL_Get_LigatureCaret_Count ( face, gid ); for ( i = 0; i < ligcount; i++ ) { /* Here, we will ignore the direction. */ div = FTL_Get_LigatureCaret_Division( face, gid, i ); div_item = gnome_canvas_item_new(gnome_canvas_root(GNOME_CANVAS(glyph_canvas)), GNOME_TYPE_CANVAS_RECT, "x1", (double)div, "y1", (double)0, "x2", (double)div, "y2", (double)DEFAULT_UNIT, "outline_color", "red", NULL); gnome_canvas_item_affine_relative( div_item, affine ); div_items = g_slist_append ( div_items, div_item ); } if ( pixbuf_item ) { affine_with_bearing[4] = (double)bbox.xMin * factor; affine_with_bearing[5] = -(double)bbox.yMax * factor; gnome_canvas_item_affine_relative( pixbuf_item, affine_with_bearing ); } } void set_trace_level( GtkAdjustment * adj, gpointer trace ) { #ifdef FT_DEBUG_LEVEL_TRACE gint index = GPOINTER_TO_INT(trace); gint level = (gint)gtk_adjustment_get_value(adj); ft_trace_levels[index] = level; #endif /* FT_DEBUG_LEVEL_TRACE */ } /* END */