/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "gdk/gdkkeysyms.h" #include "appenv.h" #include "actionarea.h" #include "buildmenu.h" #include "colormaps.h" #include "drawable.h" #include "edit_selection.h" #include "errors.h" #include "floating_sel.h" #include "gimage_mask.h" #include "gdisplay.h" #include "general.h" #include "global_edit.h" #include "interface.h" #include "palette.h" #include "selection.h" #include "text_tool.h" #include "tools.h" #include "undo.h" #define FONT_LIST_WIDTH 125 #define FONT_LIST_HEIGHT 200 #define PIXELS 0 #define POINTS 1 #define FOUNDRY 0 #define FAMILY 1 #define WEIGHT 2 #define SLANT 3 #define SET_WIDTH 4 #define SPACING 10 #define SUPERSAMPLE 3 typedef struct _TextTool TextTool; struct _TextTool { GtkWidget *shell; GtkWidget *main_vbox; GtkWidget *font_list; GtkWidget *size_menu; GtkWidget *size_text; GtkWidget *border_text; GtkWidget *text_frame; GtkWidget *the_text; GtkWidget *menus[5]; GtkWidget *option_menus[5]; GtkWidget **foundry_items; GtkWidget **weight_items; GtkWidget **slant_items; GtkWidget **set_width_items; GtkWidget **spacing_items; GtkWidget *antialias_toggle; GdkFont *font; int click_x; int click_y; int font_index; int size_type; int antialias; int foundry; int weight; int slant; int set_width; int spacing; void *gdisp_ptr; }; typedef struct _FontInfo FontInfo; struct _FontInfo { char *family; /* The font family this info struct describes. */ int *foundries; /* An array of valid foundries. */ int *weights; /* An array of valid weights. */ int *slants; /* An array of valid slants. */ int *set_widths; /* An array of valid set widths. */ int *spacings; /* An array of valid spacings */ int **combos; /* An array of valid combinations of the above 5 items */ int ncombos; /* The number of elements in the "combos" array */ link_ptr fontnames; /* An list of valid fontnames. * This is used to make sure a family/foundry/weight/slant/set_width * combination is valid. */ }; static void text_button_press (Tool *, GdkEventButton *, gpointer); static void text_button_release (Tool *, GdkEventButton *, gpointer); static void text_motion (Tool *, GdkEventMotion *, gpointer); static void text_cursor_update (Tool *, GdkEventMotion *, gpointer); static void text_control (Tool *, int, gpointer); static void text_resize_text_widget (TextTool *); static void text_create_dialog (TextTool *); static void text_ok_callback (GtkWidget *, gpointer); static void text_cancel_callback (GtkWidget *, gpointer); static gint text_delete_callback (GtkWidget *, GdkEvent *, gpointer); static void text_pixels_callback (GtkWidget *, gpointer); static void text_points_callback (GtkWidget *, gpointer); static void text_foundry_callback (GtkWidget *, gpointer); static void text_weight_callback (GtkWidget *, gpointer); static void text_slant_callback (GtkWidget *, gpointer); static void text_set_width_callback (GtkWidget *, gpointer); static void text_spacing_callback (GtkWidget *, gpointer); static gint text_size_key_function (GtkWidget *, GdkEventKey *, gpointer); static void text_antialias_update (GtkWidget *, gpointer); static void text_font_item_update (GtkWidget *, gpointer); static void text_validate_combo (TextTool *, int); static void text_get_fonts (void); static void text_insert_font (FontInfo **, int *, char *); static link_ptr text_insert_field (link_ptr, char *, int); static char* text_get_field (char *, int); static int text_field_to_index (char **, int, char *); static int text_is_xlfd_font_name (char *); static int text_get_xlfd (double, int, char *, char *, char *, char *, char *, char *, char *); static int text_load_font (TextTool *); static void text_init_render (TextTool *); static void text_gdk_image_to_region (GdkImage *, int, PixelRegion *); static int text_get_extents (char *, char *, int *, int *, int *, int *); static Layer * text_render (GImage *, int, int, int, char *, char *, int, int); static Argument * text_tool_invoker (Argument *); static Argument * text_tool_get_extents_invoker (Argument *); static ActionAreaItem action_items[] = { { "OK", text_ok_callback, NULL, NULL }, { "Cancel", text_cancel_callback, NULL, NULL }, }; static MenuItem size_metric_items[] = { { "Pixels", 0, 0, text_pixels_callback, (gpointer) PIXELS, NULL, NULL }, { "Points", 0, 0, text_points_callback, (gpointer) POINTS, NULL, NULL }, { NULL, 0, 0, NULL, NULL, NULL, NULL } }; static TextTool *the_text_tool = NULL; static FontInfo **font_info; static int nfonts = -1; static link_ptr foundries = NULL; static link_ptr weights = NULL; static link_ptr slants = NULL; static link_ptr set_widths = NULL; static link_ptr spacings = NULL; static char **foundry_array = NULL; static char **weight_array = NULL; static char **slant_array = NULL; static char **set_width_array = NULL; static char **spacing_array = NULL; static int nfoundries = 0; static int nweights = 0; static int nslants = 0; static int nset_widths = 0; static int nspacings = 0; static void *text_options = NULL; Tool* tools_new_text () { Tool * tool; if (! text_options) text_options = tools_register_no_options (TEXT, "Text Tool Options"); tool = g_malloc (sizeof (Tool)); if (!the_text_tool) { the_text_tool = g_malloc (sizeof (TextTool)); the_text_tool->shell = NULL; the_text_tool->font_list = NULL; the_text_tool->size_menu = NULL; the_text_tool->size_text = NULL; the_text_tool->border_text = NULL; the_text_tool->text_frame = NULL; the_text_tool->the_text = NULL; the_text_tool->font = NULL; the_text_tool->font_index = 0; the_text_tool->size_type = PIXELS; the_text_tool->antialias = 1; the_text_tool->foundry = 0; the_text_tool->weight = 0; the_text_tool->slant = 0; the_text_tool->set_width = 0; the_text_tool->spacing = 0; } tool->type = TEXT; tool->state = INACTIVE; tool->scroll_lock = 1; /* Do not allow scrolling */ tool->auto_snap_to = TRUE; tool->private = (void *) the_text_tool; tool->button_press_func = text_button_press; tool->button_release_func = text_button_release; tool->motion_func = text_motion; tool->arrow_keys_func = standard_arrow_keys_func; tool->cursor_update_func = text_cursor_update; tool->control_func = text_control; return tool; } void tools_free_text (Tool *tool) { } static void text_button_press (Tool *tool, GdkEventButton *bevent, gpointer gdisp_ptr) { GDisplay *gdisp; Layer *layer; TextTool *text_tool; gdisp = gdisp_ptr; text_tool = tool->private; text_tool->gdisp_ptr = gdisp_ptr; tool->state = ACTIVE; tool->gdisp_ptr = gdisp_ptr; gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &text_tool->click_x, &text_tool->click_y, TRUE, 0); if ((layer = gimage_pick_correlate_layer (gdisp->gimage, text_tool->click_x, text_tool->click_y))) /* If there is a floating selection, and this aint it, use the move tool */ if (layer_is_floating_sel (layer)) { init_edit_selection (tool, gdisp_ptr, bevent, LayerTranslate); return; } if (!text_tool->shell) text_create_dialog (text_tool); switch (gimage_base_type (gdisp->gimage)) { case RGB: case GRAY: if (!GTK_WIDGET_VISIBLE (text_tool->antialias_toggle)) gtk_widget_show (text_tool->antialias_toggle); break; case INDEXED: if (GTK_WIDGET_VISIBLE (text_tool->antialias_toggle)) gtk_widget_hide (text_tool->antialias_toggle); break; } if (!GTK_WIDGET_VISIBLE (text_tool->shell)) gtk_widget_show (text_tool->shell); } static void text_button_release (Tool *tool, GdkEventButton *bevent, gpointer gdisp_ptr) { tool->state = INACTIVE; } static void text_motion (Tool *tool, GdkEventMotion *mevent, gpointer gdisp_ptr) { } static void text_cursor_update (Tool *tool, GdkEventMotion *mevent, gpointer gdisp_ptr) { GDisplay *gdisp; Layer *layer; int x, y; gdisp = (GDisplay *) gdisp_ptr; gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, FALSE, FALSE); if ((layer = gimage_pick_correlate_layer (gdisp->gimage, x, y))) /* if there is a floating selection, and this aint it... */ if (layer_is_floating_sel (layer)) { gdisplay_install_tool_cursor (gdisp, GDK_FLEUR); return; } gdisplay_install_tool_cursor (gdisp, GDK_XTERM); } static void text_control (Tool *tool, int action, gpointer gdisp_ptr) { switch (action) { case PAUSE : break; case RESUME : break; case HALT : if (GTK_WIDGET_VISIBLE (the_text_tool->shell)) gtk_widget_hide (the_text_tool->shell); break; } } static void text_resize_text_widget (TextTool *text_tool) { GtkStyle *style; style = gtk_style_new (); gdk_font_unref (style->font); style->font = text_tool->font; gdk_font_ref (style->font); gtk_widget_set_style (text_tool->the_text, style); } static void text_create_dialog (TextTool *text_tool) { GtkWidget *top_hbox; GtkWidget *right_vbox; GtkWidget *text_hbox; GtkWidget *list_box; GtkWidget *list_item; GtkWidget *size_opt_menu; GtkWidget *border_label; GtkWidget *menu_table; GtkWidget *menu_label; GtkWidget **menu_items[5]; GtkWidget *alignment; int nmenu_items[5]; char *menu_strs[5]; char **menu_item_strs[5]; int *font_infos[5]; MenuItemCallback menu_callbacks[5]; int i, j; /* Create the shell and vertical & horizontal boxes */ text_tool->shell = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (text_tool->shell), "Text Tool"); gtk_window_set_policy (GTK_WINDOW (text_tool->shell), FALSE, TRUE, TRUE); gtk_window_position (GTK_WINDOW (text_tool->shell), GTK_WIN_POS_MOUSE); /* handle the wm close signal */ gtk_signal_connect (GTK_OBJECT (text_tool->shell), "delete_event", GTK_SIGNAL_FUNC (text_delete_callback), text_tool); text_tool->main_vbox = gtk_vbox_new (FALSE, 2); gtk_container_border_width (GTK_CONTAINER (text_tool->main_vbox), 2); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (text_tool->shell)->vbox), text_tool->main_vbox, TRUE, TRUE, 0); top_hbox = gtk_hbox_new (FALSE, 1); gtk_box_pack_start (GTK_BOX (text_tool->main_vbox), top_hbox, TRUE, TRUE, 0); /* Create the font listbox */ list_box = gtk_scrolled_window_new (NULL, NULL); gtk_widget_set_usize (list_box, FONT_LIST_WIDTH, FONT_LIST_HEIGHT); gtk_box_pack_start (GTK_BOX (top_hbox), list_box, TRUE, TRUE, 0); text_tool->font_list = gtk_list_new (); gtk_container_add (GTK_CONTAINER (list_box), text_tool->font_list); gtk_list_set_selection_mode (GTK_LIST (text_tool->font_list), GTK_SELECTION_BROWSE); if (nfonts == -1) text_get_fonts (); for (i = 0; i < nfonts; i++) { list_item = gtk_list_item_new_with_label (font_info[i]->family); gtk_container_add (GTK_CONTAINER (text_tool->font_list), list_item); gtk_signal_connect (GTK_OBJECT (list_item), "select", (GtkSignalFunc) text_font_item_update, (gpointer) ((long) i)); gtk_widget_show (list_item); } gtk_widget_show (text_tool->font_list); /* Create the box to hold options */ right_vbox = gtk_vbox_new (FALSE, 2); gtk_box_pack_start (GTK_BOX (top_hbox), right_vbox, TRUE, TRUE, 2); /* Create the text hbox, size text, and fonts size metric option menu */ text_hbox = gtk_hbox_new (FALSE, 2); gtk_box_pack_start (GTK_BOX (right_vbox), text_hbox, FALSE, FALSE, 0); text_tool->size_text = gtk_entry_new (); gtk_widget_set_usize (text_tool->size_text, 75, 0); gtk_entry_set_text (GTK_ENTRY (text_tool->size_text), "50"); gtk_signal_connect (GTK_OBJECT (text_tool->size_text), "key_press_event", (GtkSignalFunc) text_size_key_function, text_tool); gtk_box_pack_start (GTK_BOX (text_hbox), text_tool->size_text, TRUE, TRUE, 0); /* Create the size menu */ size_metric_items[0].user_data = text_tool; size_metric_items[1].user_data = text_tool; text_tool->size_menu = build_menu (size_metric_items, NULL); size_opt_menu = gtk_option_menu_new (); gtk_box_pack_start (GTK_BOX (text_hbox), size_opt_menu, FALSE, FALSE, 0); /* create the text entry widget */ text_tool->text_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (text_tool->text_frame), GTK_SHADOW_NONE); gtk_box_pack_start (GTK_BOX (text_tool->main_vbox), text_tool->text_frame, FALSE, FALSE, 2); text_tool->the_text = gtk_entry_new (); gtk_container_add (GTK_CONTAINER (text_tool->text_frame), text_tool->the_text); /* create the antialiasing toggle button */ text_tool->antialias_toggle = gtk_check_button_new_with_label ("Antialiasing"); gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (text_tool->antialias_toggle), text_tool->antialias); gtk_box_pack_start (GTK_BOX (right_vbox), text_tool->antialias_toggle, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (text_tool->antialias_toggle), "toggled", (GtkSignalFunc) text_antialias_update, text_tool); /* Allocate the arrays for the foundry, weight, slant, set_width and spacing menu items */ text_tool->foundry_items = (GtkWidget **) g_malloc (sizeof (GtkWidget *) * nfoundries); text_tool->weight_items = (GtkWidget **) g_malloc (sizeof (GtkWidget *) * nweights); text_tool->slant_items = (GtkWidget **) g_malloc (sizeof (GtkWidget *) * nslants); text_tool->set_width_items = (GtkWidget **) g_malloc (sizeof (GtkWidget *) * nset_widths); text_tool->spacing_items = (GtkWidget **) g_malloc (sizeof (GtkWidget *) * nspacings); menu_items[0] = text_tool->foundry_items; menu_items[1] = text_tool->weight_items; menu_items[2] = text_tool->slant_items; menu_items[3] = text_tool->set_width_items; menu_items[4] = text_tool->spacing_items; nmenu_items[0] = nfoundries; nmenu_items[1] = nweights; nmenu_items[2] = nslants; nmenu_items[3] = nset_widths; nmenu_items[4] = nspacings; menu_strs[0] = "Foundry"; menu_strs[1] = "Weight"; menu_strs[2] = "Slant"; menu_strs[3] = "Set width"; menu_strs[4] = "Spacing"; menu_item_strs[0] = foundry_array; menu_item_strs[1] = weight_array; menu_item_strs[2] = slant_array; menu_item_strs[3] = set_width_array; menu_item_strs[4] = spacing_array; menu_callbacks[0] = text_foundry_callback; menu_callbacks[1] = text_weight_callback; menu_callbacks[2] = text_slant_callback; menu_callbacks[3] = text_set_width_callback; menu_callbacks[4] = text_spacing_callback; font_infos[0] = font_info[0]->foundries; font_infos[1] = font_info[0]->weights; font_infos[2] = font_info[0]->slants; font_infos[3] = font_info[0]->set_widths; font_infos[4] = font_info[0]->spacings; menu_table = gtk_table_new (6, 2, FALSE); gtk_box_pack_start (GTK_BOX (right_vbox), menu_table, TRUE, TRUE, 0); /* Create the other menus */ for (i = 0; i < 5; i++) { menu_label = gtk_label_new (menu_strs[i]); gtk_misc_set_alignment (GTK_MISC (menu_label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (menu_table), menu_label, 0, 1, i, i + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 1); alignment = gtk_alignment_new (0.0, 0.0, 0.0, 0.0); gtk_table_attach (GTK_TABLE (menu_table), alignment, 1, 2, i, i + 1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_SHRINK, 1, 1); text_tool->menus[i] = gtk_menu_new (); for (j = 0; j < nmenu_items[i]; j++) { menu_items[i][j] = gtk_menu_item_new_with_label (menu_item_strs[i][j]); gtk_widget_set_sensitive (menu_items[i][j], font_infos[i][j]); gtk_container_add (GTK_CONTAINER (text_tool->menus[i]), menu_items[i][j]); gtk_signal_connect (GTK_OBJECT (menu_items[i][j]), "activate", (GtkSignalFunc) menu_callbacks[i], (gpointer) ((long) j)); gtk_widget_show (menu_items[i][j]); } text_tool->option_menus[i] = gtk_option_menu_new (); gtk_container_add (GTK_CONTAINER (alignment), text_tool->option_menus[i]); gtk_widget_show (menu_label); gtk_widget_show (text_tool->option_menus[i]); gtk_widget_show (alignment); gtk_option_menu_set_menu (GTK_OPTION_MENU (text_tool->option_menus[i]), text_tool->menus[i]); } /* Create the border text hbox, border text, and label */ border_label = gtk_label_new ("Border"); gtk_misc_set_alignment (GTK_MISC (border_label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (menu_table), border_label, 0, 1, i, i + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 1); alignment = gtk_alignment_new (0.0, 0.0, 0.0, 0.0); gtk_table_attach (GTK_TABLE (menu_table), alignment, 1, 2, i, i + 1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_SHRINK, 1, 1); text_tool->border_text = gtk_entry_new (); gtk_widget_set_usize (text_tool->border_text, 75, 25); gtk_entry_set_text (GTK_ENTRY (text_tool->border_text), "0"); gtk_container_add (GTK_CONTAINER (alignment), text_tool->border_text); gtk_widget_show (alignment); /* Create the action area */ action_items[0].user_data = text_tool; action_items[1].user_data = text_tool; build_action_area (GTK_DIALOG (text_tool->shell), action_items, 2, 0); /* Show the widgets */ gtk_widget_show (menu_table); gtk_widget_show (text_tool->antialias_toggle); gtk_widget_show (text_tool->size_text); gtk_widget_show (border_label); gtk_widget_show (text_tool->border_text); gtk_widget_show (size_opt_menu); gtk_widget_show (text_hbox); gtk_widget_show (list_box); gtk_widget_show (right_vbox); gtk_widget_show (top_hbox); gtk_widget_show (text_tool->the_text); gtk_widget_show (text_tool->text_frame); gtk_widget_show (text_tool->main_vbox); /* Post initialization */ gtk_option_menu_set_menu (GTK_OPTION_MENU (size_opt_menu), text_tool->size_menu); if (nfonts) text_load_font (text_tool); /* Show the shell */ gtk_widget_show (text_tool->shell); } static void text_ok_callback (GtkWidget *w, gpointer client_data) { TextTool * text_tool; text_tool = (TextTool *) client_data; if (GTK_WIDGET_VISIBLE (text_tool->shell)) gtk_widget_hide (text_tool->shell); text_init_render (text_tool); } static gint text_delete_callback (GtkWidget *w, GdkEvent *e, gpointer client_data) { text_cancel_callback (w, client_data); return FALSE; } static void text_cancel_callback (GtkWidget *w, gpointer client_data) { TextTool * text_tool; text_tool = (TextTool *) client_data; if (GTK_WIDGET_VISIBLE (text_tool->shell)) gtk_widget_hide (text_tool->shell); } static void text_font_item_update (GtkWidget *w, gpointer data) { FontInfo *font; int old_index; int i, index; /* Is this font being selected? */ if (w->state != GTK_STATE_SELECTED) return; index = (long) data; old_index = the_text_tool->font_index; the_text_tool->font_index = index; if (!text_load_font (the_text_tool)) { the_text_tool->font_index = old_index; return; } font = font_info[the_text_tool->font_index]; if (the_text_tool->foundry && !font->foundries[the_text_tool->foundry]) { the_text_tool->foundry = 0; gtk_option_menu_set_history (GTK_OPTION_MENU (the_text_tool->option_menus[0]), 0); } if (the_text_tool->weight && !font->weights[the_text_tool->weight]) { the_text_tool->weight = 0; gtk_option_menu_set_history (GTK_OPTION_MENU (the_text_tool->option_menus[1]), 0); } if (the_text_tool->slant && !font->slants[the_text_tool->slant]) { the_text_tool->slant = 0; gtk_option_menu_set_history (GTK_OPTION_MENU (the_text_tool->option_menus[2]), 0); } if (the_text_tool->set_width && !font->set_widths[the_text_tool->set_width]) { the_text_tool->set_width = 0; gtk_option_menu_set_history (GTK_OPTION_MENU (the_text_tool->option_menus[3]), 0); } if (the_text_tool->spacing && !font->spacings[the_text_tool->spacing]) { the_text_tool->spacing = 0; gtk_option_menu_set_history (GTK_OPTION_MENU (the_text_tool->option_menus[4]), 0); } for (i = 0; i < nfoundries; i++) gtk_widget_set_sensitive (the_text_tool->foundry_items[i], font->foundries[i]); for (i = 0; i < nweights; i++) gtk_widget_set_sensitive (the_text_tool->weight_items[i], font->weights[i]); for (i = 0; i < nslants; i++) gtk_widget_set_sensitive (the_text_tool->slant_items[i], font->slants[i]); for (i = 0; i < nset_widths; i++) gtk_widget_set_sensitive (the_text_tool->set_width_items[i], font->set_widths[i]); for (i = 0; i < nspacings; i++) gtk_widget_set_sensitive (the_text_tool->spacing_items[i], font->spacings[i]); } static void text_pixels_callback (GtkWidget *w, gpointer client_data) { TextTool *text_tool; int old_value; text_tool = (TextTool *) client_data; old_value = text_tool->size_type; text_tool->size_type = PIXELS; if (!text_load_font (text_tool)) text_tool->size_type = old_value; } static void text_points_callback (GtkWidget *w, gpointer client_data) { TextTool *text_tool; int old_value; text_tool = (TextTool *) client_data; old_value = text_tool->size_type; text_tool->size_type = POINTS; if (!text_load_font (text_tool)) text_tool->size_type = old_value; } static void text_foundry_callback (GtkWidget *w, gpointer client_data) { int old_value; old_value = the_text_tool->foundry; the_text_tool->foundry = (long) client_data; text_validate_combo (the_text_tool, 0); if (text_load_font (the_text_tool)) the_text_tool->foundry = old_value; } static void text_weight_callback (GtkWidget *w, gpointer client_data) { int old_value; old_value = the_text_tool->weight; the_text_tool->weight = (long) client_data; text_validate_combo (the_text_tool, 1); if (!text_load_font (the_text_tool)) the_text_tool->weight = old_value; } static void text_slant_callback (GtkWidget *w, gpointer client_data) { int old_value; old_value = the_text_tool->slant; the_text_tool->slant = (long) client_data; text_validate_combo (the_text_tool, 2); if (!text_load_font (the_text_tool)) the_text_tool->slant = old_value; } static void text_set_width_callback (GtkWidget *w, gpointer client_data) { int old_value; old_value = the_text_tool->set_width; the_text_tool->set_width = (long) client_data; text_validate_combo (the_text_tool, 3); if (!text_load_font (the_text_tool)) the_text_tool->set_width = old_value; } static void text_spacing_callback (GtkWidget *w, gpointer client_data) { int old_value; old_value = the_text_tool->spacing; the_text_tool->spacing = (long) client_data; text_validate_combo (the_text_tool, 4); if (!text_load_font (the_text_tool)) the_text_tool->spacing = old_value; } static gint text_size_key_function (GtkWidget *w, GdkEventKey *event, gpointer data) { TextTool *text_tool; char buffer[16]; int old_value; text_tool = (TextTool *) data; if (event->keyval == GDK_Return) { gtk_signal_emit_stop_by_name (GTK_OBJECT (w), "key_press_event"); old_value = atoi (gtk_entry_get_text (GTK_ENTRY (text_tool->size_text))); if (!text_load_font (text_tool)) { sprintf (buffer, "%d", old_value); gtk_entry_set_text (GTK_ENTRY (text_tool->size_text), buffer); } return TRUE; } return FALSE; } static void text_antialias_update (GtkWidget *w, gpointer data) { TextTool *text_tool; text_tool = (TextTool *) data; if (GTK_TOGGLE_BUTTON (w)->active) text_tool->antialias = TRUE; else text_tool->antialias = FALSE; } static void text_validate_combo (TextTool *text_tool, int which) { FontInfo *font; int which_val; int new_combo[5]; int best_combo[5]; int best_matches; int matches; int i; font = font_info[text_tool->font_index]; switch (which) { case 0: which_val = text_tool->foundry; break; case 1: which_val = text_tool->weight; break; case 2: which_val = text_tool->slant; break; case 3: which_val = text_tool->set_width; break; case 4: which_val = text_tool->spacing; break; default: which_val = 0; break; } best_matches = -1; best_combo[0] = 0; best_combo[1] = 0; best_combo[2] = 0; best_combo[3] = 0; best_combo[4] = 0; for (i = 0; i < font->ncombos; i++) { /* we must match the which field */ if (font->combos[i][which] == which_val) { matches = 0; new_combo[0] = 0; new_combo[1] = 0; new_combo[2] = 0; new_combo[3] = 0; new_combo[4] = 0; if ((text_tool->foundry == 0) || (text_tool->foundry == font->combos[i][0])) { matches++; if (text_tool->foundry) new_combo[0] = font->combos[i][0]; } if ((text_tool->weight == 0) || (text_tool->weight == font->combos[i][1])) { matches++; if (text_tool->weight) new_combo[1] = font->combos[i][1]; } if ((text_tool->slant == 0) || (text_tool->slant == font->combos[i][2])) { matches++; if (text_tool->slant) new_combo[2] = font->combos[i][2]; } if ((text_tool->set_width == 0) || (text_tool->set_width == font->combos[i][3])) { matches++; if (text_tool->set_width) new_combo[3] = font->combos[i][3]; } if ((text_tool->spacing == 0) || (text_tool->spacing == font->combos[i][4])) { matches++; if (text_tool->spacing) new_combo[4] = font->combos[i][4]; } /* if we get all 5 matches simply return */ if (matches == 5) return; if (matches > best_matches) { best_matches = matches; best_combo[0] = new_combo[0]; best_combo[1] = new_combo[1]; best_combo[2] = new_combo[2]; best_combo[3] = new_combo[3]; best_combo[4] = new_combo[4]; } } } if (best_matches > -1) { if (text_tool->foundry != best_combo[0]) { text_tool->foundry = best_combo[0]; if (which != 0) gtk_option_menu_set_history (GTK_OPTION_MENU (text_tool->option_menus[0]), text_tool->foundry); } if (text_tool->weight != best_combo[1]) { text_tool->weight = best_combo[1]; if (which != 1) gtk_option_menu_set_history (GTK_OPTION_MENU (text_tool->option_menus[1]), text_tool->weight); } if (text_tool->slant != best_combo[2]) { text_tool->slant = best_combo[2]; if (which != 2) gtk_option_menu_set_history (GTK_OPTION_MENU (text_tool->option_menus[2]), text_tool->slant); } if (text_tool->set_width != best_combo[3]) { text_tool->set_width = best_combo[3]; if (which != 3) gtk_option_menu_set_history (GTK_OPTION_MENU (text_tool->option_menus[3]), text_tool->set_width); } if (text_tool->spacing != best_combo[4]) { text_tool->spacing = best_combo[4]; if (which != 4) gtk_option_menu_set_history (GTK_OPTION_MENU (text_tool->option_menus[4]), text_tool->spacing); } } } static void text_get_fonts () { char **fontnames; char *fontname; char *field; link_ptr temp_list; int num_fonts; int index; int i, j; /* construct a valid font pattern */ fontnames = XListFonts (DISPLAY, "-*-*-*-*-*-*-0-0-75-75-*-0-*-*", 32767, &num_fonts); /* the maximum size of the table is the number of font names returned */ font_info = g_malloc (sizeof (FontInfo**) * num_fonts); /* insert the fontnames into a table */ nfonts = 0; for (i = 0; i < num_fonts; i++) if (text_is_xlfd_font_name (fontnames[i])) { text_insert_font (font_info, &nfonts, fontnames[i]); foundries = text_insert_field (foundries, fontnames[i], FOUNDRY); weights = text_insert_field (weights, fontnames[i], WEIGHT); slants = text_insert_field (slants, fontnames[i], SLANT); set_widths = text_insert_field (set_widths, fontnames[i], SET_WIDTH); spacings = text_insert_field (spacings, fontnames[i], SPACING); } XFreeFontNames (fontnames); nfoundries = list_length (foundries) + 1; nweights = list_length (weights) + 1; nslants = list_length (slants) + 1; nset_widths = list_length (set_widths) + 1; nspacings = list_length (spacings) + 1; foundry_array = g_malloc (sizeof (char*) * nfoundries); weight_array = g_malloc (sizeof (char*) * nweights); slant_array = g_malloc (sizeof (char*) * nslants); set_width_array = g_malloc (sizeof (char*) * nset_widths); spacing_array = g_malloc (sizeof (char*) * nspacings); i = 1; temp_list = foundries; while (temp_list) { foundry_array[i++] = temp_list->data; temp_list = temp_list->next; } i = 1; temp_list = weights; while (temp_list) { weight_array[i++] = temp_list->data; temp_list = temp_list->next; } i = 1; temp_list = slants; while (temp_list) { slant_array[i++] = temp_list->data; temp_list = temp_list->next; } i = 1; temp_list = set_widths; while (temp_list) { set_width_array[i++] = temp_list->data; temp_list = temp_list->next; } i = 1; temp_list = spacings; while (temp_list) { spacing_array[i++] = temp_list->data; temp_list = temp_list->next; } foundry_array[0] = "*"; weight_array[0] = "*"; slant_array[0] = "*"; set_width_array[0] = "*"; spacing_array[0] = "*"; for (i = 0; i < nfonts; i++) { font_info[i]->foundries = g_malloc (sizeof (int) * nfoundries); font_info[i]->weights = g_malloc (sizeof (int) * nweights); font_info[i]->slants = g_malloc (sizeof (int) * nslants); font_info[i]->set_widths = g_malloc (sizeof (int) * nset_widths); font_info[i]->spacings = g_malloc (sizeof (int) * nspacings); font_info[i]->ncombos = list_length (font_info[i]->fontnames); font_info[i]->combos = g_malloc (sizeof (int*) * font_info[i]->ncombos); for (j = 0; j < nfoundries; j++) font_info[i]->foundries[j] = 0; for (j = 0; j < nweights; j++) font_info[i]->weights[j] = 0; for (j = 0; j < nslants; j++) font_info[i]->slants[j] = 0; for (j = 0; j < nset_widths; j++) font_info[i]->set_widths[j] = 0; for (j = 0; j < nspacings; j++) font_info[i]->spacings[j] = 0; font_info[i]->foundries[0] = 1; font_info[i]->weights[0] = 1; font_info[i]->slants[0] = 1; font_info[i]->set_widths[0] = 1; font_info[i]->spacings[0] = 1; j = 0; temp_list = font_info[i]->fontnames; while (temp_list) { fontname = temp_list->data; temp_list = temp_list->next; font_info[i]->combos[j] = g_malloc (sizeof (int) * 5); field = text_get_field (fontname, FOUNDRY); index = text_field_to_index (foundry_array, nfoundries, field); font_info[i]->foundries[index] = 1; font_info[i]->combos[j][0] = index; free (field); field = text_get_field (fontname, WEIGHT); index = text_field_to_index (weight_array, nweights, field); font_info[i]->weights[index] = 1; font_info[i]->combos[j][1] = index; free (field); field = text_get_field (fontname, SLANT); index = text_field_to_index (slant_array, nslants, field); font_info[i]->slants[index] = 1; font_info[i]->combos[j][2] = index; free (field); field = text_get_field (fontname, SET_WIDTH); index = text_field_to_index (set_width_array, nset_widths, field); font_info[i]->set_widths[index] = 1; font_info[i]->combos[j][3] = index; free (field); field = text_get_field (fontname, SPACING); index = text_field_to_index (spacing_array, nspacings, field); font_info[i]->spacings[index] = 1; font_info[i]->combos[j][4] = index; free (field); j += 1; } } } static void text_insert_font (FontInfo **table, int *ntable, char *fontname) { FontInfo *temp_info; char *family; int lower, upper; int middle, cmp; /* insert a fontname into a table */ family = text_get_field (fontname, FAMILY); if (!family) return; lower = 0; if (*ntable > 0) { /* Do a binary search to determine if we have already encountered * a font from this family. */ upper = *ntable; while (lower < upper) { middle = (lower + upper) >> 1; cmp = strcmp (family, table[middle]->family); if (cmp == 0) { table[middle]->fontnames = add_to_list (table[middle]->fontnames, g_strdup (fontname)); return; } else if (cmp < 0) upper = middle; else if (cmp > 0) lower = middle+1; } } /* Add another entry to the table for this new font family */ table[*ntable] = g_malloc (sizeof (FontInfo)); table[*ntable]->family = family; table[*ntable]->foundries = NULL; table[*ntable]->weights = NULL; table[*ntable]->slants = NULL; table[*ntable]->set_widths = NULL; table[*ntable]->fontnames = NULL; table[*ntable]->fontnames = add_to_list (table[*ntable]->fontnames, g_strdup (fontname)); (*ntable)++; /* Quickly insert the entry into the table in sorted order * using a modification of insertion sort and the knowledge * that the entries proper position in the table was determined * above in the binary search and is contained in the "lower" * variable. */ if (*ntable > 1) { temp_info = table[*ntable - 1]; upper = *ntable - 1; while (lower != upper) { table[upper] = table[upper-1]; upper -= 1; } table[lower] = temp_info; } } static link_ptr text_insert_field (link_ptr list, char *fontname, int field_num) { link_ptr temp_list; link_ptr prev_list; link_ptr new_list; char *field; int cmp; field = text_get_field (fontname, field_num); if (!field) return list; temp_list = list; prev_list = NULL; while (temp_list) { cmp = strcmp (field, temp_list->data); if (cmp == 0) { free (field); return list; } else if (cmp < 0) { new_list = alloc_list (); new_list->data = field; new_list->next = temp_list; if (prev_list) { prev_list->next = new_list; return list; } else return new_list; } else { prev_list = temp_list; temp_list = temp_list->next; } } new_list = alloc_list (); new_list->data = field; new_list->next = NULL; if (prev_list) { prev_list->next = new_list; return list; } else return new_list; } static char* text_get_field (char *fontname, int field_num) { char *t1, *t2; char *field; /* we assume this is a valid fontname...that is, it has 14 fields */ t1 = fontname; while (*t1 && (field_num >= 0)) if (*t1++ == '-') field_num--; t2 = t1; while (*t2 && (*t2 != '-')) t2++; if (t1 != t2) { field = g_malloc (1 + (long) t2 - (long) t1); strncpy (field, t1, (long) t2 - (long) t1); field[(long) t2 - (long) t1] = 0; return field; } return g_strdup ("(nil)"); } static int text_field_to_index (char **table, int ntable, char *field) { int i; for (i = 0; i < ntable; i++) if (strcmp (field, table[i]) == 0) return i; return -1; } static int text_is_xlfd_font_name (char *fontname) { int i; i = 0; while (*fontname) if (*fontname++ == '-') i++; return (i == 14); } static int text_get_xlfd (double size, int size_type, char *foundry, char *family, char *weight, char *slant, char *set_width, char *spacing, char *fontname) { char pixel_size[12], point_size[12]; if (size > 0) { switch (size_type) { case PIXELS: sprintf (pixel_size, "%d", (int) size); sprintf (point_size, "*"); break; case POINTS: sprintf (pixel_size, "*"); sprintf (point_size, "%d", (int) (size * 10)); break; } /* create the fontname */ sprintf (fontname, "-%s-%s-%s-%s-%s-*-%s-%s-75-75-%s-*-*-*", foundry, family, weight, slant, set_width, pixel_size, point_size, spacing); return TRUE; } else return FALSE; } static int text_load_font (TextTool *text_tool) { GdkFont *font; char fontname[2048]; double size; char *size_text; char *foundry_str; char *family_str; char *weight_str; char *slant_str; char *set_width_str; char *spacing_str; size_text = gtk_entry_get_text (GTK_ENTRY (text_tool->size_text)); size = atof (size_text); foundry_str = foundry_array[text_tool->foundry]; if (strcmp (foundry_str, "(nil)") == 0) foundry_str = ""; family_str = font_info[text_tool->font_index]->family; weight_str = weight_array[text_tool->weight]; if (strcmp (weight_str, "(nil)") == 0) weight_str = ""; slant_str = slant_array[text_tool->slant]; if (strcmp (slant_str, "(nil)") == 0) slant_str = ""; set_width_str = set_width_array[text_tool->set_width]; if (strcmp (set_width_str, "(nil)") == 0) set_width_str = ""; spacing_str = spacing_array[text_tool->spacing]; if (strcmp (spacing_str, "(nil)") == 0) spacing_str = ""; if (text_get_xlfd (size, text_tool->size_type, foundry_str, family_str, weight_str, slant_str, set_width_str, spacing_str, fontname)) { font = gdk_font_load (fontname); if (font) { if (text_tool->font) gdk_font_unref (text_tool->font); text_tool->font = font; text_resize_text_widget (text_tool); return TRUE; } } return FALSE; } static void text_init_render (TextTool *text_tool) { GDisplay *gdisp; char fontname[2048]; char *text; int border; char *border_text; double size; char *size_text; char *foundry_str; char *family_str; char *weight_str; char *slant_str; char *set_width_str; char *spacing_str; size_text = gtk_entry_get_text (GTK_ENTRY (text_tool->size_text)); size = atof (size_text); if (text_tool->antialias) size *= SUPERSAMPLE; foundry_str = foundry_array[text_tool->foundry]; if (strcmp (foundry_str, "(nil)") == 0) foundry_str = ""; family_str = font_info[text_tool->font_index]->family; weight_str = weight_array[text_tool->weight]; if (strcmp (weight_str, "(nil)") == 0) weight_str = ""; slant_str = slant_array[text_tool->slant]; if (strcmp (slant_str, "(nil)") == 0) slant_str = ""; set_width_str = set_width_array[text_tool->set_width]; if (strcmp (set_width_str, "(nil)") == 0) set_width_str = ""; spacing_str = spacing_array[text_tool->spacing]; if (strcmp (spacing_str, "(nil)") == 0) spacing_str = ""; if (text_get_xlfd (size, text_tool->size_type, foundry_str, family_str, weight_str, slant_str, set_width_str, spacing_str, fontname)) { /* get the text */ text = gtk_entry_get_text (GTK_ENTRY (text_tool->the_text)); border_text = gtk_entry_get_text (GTK_ENTRY (text_tool->border_text)); border = atoi (border_text); gdisp = (GDisplay *) text_tool->gdisp_ptr; text_render (gdisp->gimage, gimage_active_drawable (gdisp->gimage), text_tool->click_x, text_tool->click_y, fontname, text, border, text_tool->antialias); gdisplays_flush (); } } static void text_gdk_image_to_region (GdkImage *image, int scale, PixelRegion *textPR) { int black_pixel; int pixel; int value; int scalex, scaley; int scale2; int x, y; int i, j; unsigned char * data; scale2 = scale * scale; black_pixel = BlackPixel (DISPLAY, DefaultScreen (DISPLAY)); data = textPR->data; for (y = 0, scaley = 0; y < textPR->h; y++, scaley += scale) { for (x = 0, scalex = 0; x < textPR->w; x++, scalex += scale) { value = 0; for (i = scaley; i < scaley + scale; i++) for (j = scalex; j < scalex + scale; j++) { pixel = gdk_image_get_pixel (image, j, i); if (pixel == black_pixel) value += 255; } value = value / scale2; /* store the alpha value in the data */ *data++ = (unsigned char) value; } } } static Layer * text_render (GImage *gimage, int drawable_id, int text_x, int text_y, char *fontname, char *text, int border, int antialias) { GdkFont *font; GdkPixmap *pixmap; GdkImage *image; GdkGC *gc; GdkColor black, white; Layer *layer; TileManager *mask, *newmask; PixelRegion textPR, maskPR; int layer_type; unsigned char color[MAX_CHANNELS]; char *str; int nstrs; int line_width, line_height; int pixmap_width, pixmap_height; int text_width, text_height; int width, height; int x, y, k; void * pr; /* determine the layer type */ if (drawable_id != -1) layer_type = drawable_type_with_alpha (drawable_id); else layer_type = gimage_base_type_with_alpha (gimage); /* scale the text based on the antialiasing amount */ if (antialias) antialias = SUPERSAMPLE; else antialias = 1; /* load the font in */ font = gdk_font_load (fontname); if (!font) return NULL; /* determine the bounding box of the text */ width = -1; height = 0; line_height = font->ascent + font->descent; nstrs = 0; str = strtok (text, "\n"); while (str) { nstrs += 1; /* gdk_string_measure will give the correct width of the * string. However, we'll add a little "fudge" factor just * to be sure. */ line_width = gdk_string_measure (font, str) + 5; if (line_width > width) width = line_width; height += line_height; str = strtok (NULL, "\n"); } /* We limit the largest pixmap we create to approximately 200x200. * This is approximate since it depends on the amount of antialiasing. * Basically, we want the width and height to be divisible by the antialiasing * amount. (Which lies in the range 1-10). * This avoids problems on some X-servers (Xinside) which have problems * with large pixmaps. (Specifically pixmaps which are larger - width * or height - than the screen). */ pixmap_width = TILE_WIDTH * antialias; pixmap_height = TILE_HEIGHT * antialias; /* determine the actual text size based on the amount of antialiasing */ text_width = width / antialias; text_height = height / antialias; /* create the pixmap of depth 1 */ pixmap = gdk_pixmap_new (NULL, pixmap_width, pixmap_height, 1); /* create the gc */ gc = gdk_gc_new (pixmap); gdk_gc_set_font (gc, font); /* get black and white pixels for this gdisplay */ black.pixel = BlackPixel (DISPLAY, DefaultScreen (DISPLAY)); white.pixel = WhitePixel (DISPLAY, DefaultScreen (DISPLAY)); /* Render the text into the pixmap. * Since the pixmap may not fully bound the text (because we limit its size) * we must tile it around the texts actual bounding box. */ mask = tile_manager_new (text_width, text_height, 1); pixel_region_init (&maskPR, mask, 0, 0, text_width, text_height, TRUE); for (pr = pixel_regions_register (1, &maskPR); pr != NULL; pr = pixel_regions_process (pr)) { /* erase the pixmap */ gdk_gc_set_foreground (gc, &white); gdk_draw_rectangle (pixmap, gc, 1, 0, 0, pixmap_width, pixmap_height); gdk_gc_set_foreground (gc, &black); /* adjust the x and y values */ x = -maskPR.x * antialias; y = font->ascent - maskPR.y * antialias; str = text; for (k = 0; k < nstrs; k++) { gdk_draw_string (pixmap, font, gc, x, y, str); str += strlen (str) + 1; y += line_height; } /* create the GdkImage */ image = gdk_image_get (pixmap, 0, 0, pixmap_width, pixmap_height); if (!image) fatal_error ("sanity check failed: could not get gdk image"); if (image->depth != 1) fatal_error ("sanity check failed: image should have 1 bit per pixel"); /* convert the GdkImage bitmap to a region */ text_gdk_image_to_region (image, antialias, &maskPR); /* free the image */ gdk_image_destroy (image); } /* Crop the mask buffer */ newmask = crop_buffer (mask, border); if (newmask != mask) tile_manager_destroy (mask); if (newmask && (layer = layer_new (gimage->ID, newmask->levels[0].width, newmask->levels[0].height, layer_type, "Text Layer", OPAQUE, NORMAL_MODE))) { /* color the layer buffer */ gimage_get_foreground (gimage, drawable_id, color); color[layer->bytes - 1] = OPAQUE; pixel_region_init (&textPR, layer->tiles, 0, 0, layer->width, layer->height, TRUE); color_region (&textPR, color); /* apply the text mask */ pixel_region_init (&textPR, layer->tiles, 0, 0, layer->width, layer->height, TRUE); pixel_region_init (&maskPR, newmask, 0, 0, layer->width, layer->height, FALSE); apply_mask_to_region (&textPR, &maskPR, OPAQUE); /* Start a group undo */ undo_push_group_start (gimage, EDIT_PASTE_UNDO); /* Set the layer offsets */ layer->offset_x = text_x; layer->offset_y = text_y; /* If there is a selection mask clear it-- * this might not always be desired, but in general, * it seems like the correct behavior. */ if (! gimage_mask_is_empty (gimage)) channel_clear (gimage_get_mask (gimage)); /* If the drawable id is invalid, create a new layer */ if (drawable_id == -1) gimage_add_layer (gimage, layer, -1); /* Otherwise, instantiate the text as the new floating selection */ else floating_sel_attach (layer, drawable_id); /* end the group undo */ undo_push_group_end (gimage); tile_manager_destroy (newmask); } else { if (newmask) warning("text_render: could not allocate image"); layer = NULL; } /* free the pixmap */ gdk_pixmap_unref (pixmap); /* free the gc */ gdk_gc_destroy (gc); /* free the font */ gdk_font_unref (font); return layer; } static int text_get_extents (char *fontname, char *text, int *width, int *height, int *ascent, int *descent) { GdkFont *font; char *str; int nstrs; int line_width, line_height; /* load the font in */ font = gdk_font_load (fontname); if (!font) return FALSE; /* determine the bounding box of the text */ *width = -1; *height = 0; *ascent = font->ascent; *descent = font->descent; line_height = *ascent + *descent; nstrs = 0; str = strtok (text, "\n"); while (str) { nstrs += 1; /* gdk_string_measure will give the correct width of the * string. However, we'll add a little "fudge" factor just * to be sure. */ line_width = gdk_string_measure (font, str) + 5; if (line_width > *width) *width = line_width; *height += line_height; str = strtok (NULL, "\n"); } if (*width < 0) return FALSE; else return TRUE; } /* The text_tool procedure definition */ ProcArg text_tool_args[] = { { PDB_IMAGE, "image", "The image" }, { PDB_DRAWABLE, "drawable", "The affected drawable: (-1 for a new text layer)" }, { PDB_FLOAT, "x", "the x coordinate for the left side of text bounding box" }, { PDB_FLOAT, "y", "the y coordinate for the top of text bounding box" }, { PDB_STRING, "text", "the text to generate" }, { PDB_INT32, "border", "the size of the border: border >= 0" }, { PDB_INT32, "antialias", "generate antialiased text" }, { PDB_FLOAT, "size", "the size of text in either pixels or points" }, { PDB_INT32, "size_type", "the units of the specified size: { PIXELS (0), POINTS (1) }" }, { PDB_STRING, "foundry", "the font foundry, \"*\" for any" }, { PDB_STRING, "family", "the font family, \"*\" for any" }, { PDB_STRING, "weight", "the font weight, \"*\" for any" }, { PDB_STRING, "slant", "the font slant, \"*\" for any" }, { PDB_STRING, "set_width", "the font set-width parameter, \"*\" for any" }, { PDB_STRING, "spacing", "the font spacing, \"*\" for any" } }; ProcArg text_tool_out_args[] = { { PDB_LAYER, "text_layer", "the new text layer" } }; ProcRecord text_tool_proc = { "gimp_text", "Add text at the specified location as a floating selection or a new layer.", "This tool requires font information in the form of seven parameters: {size, foundry, family, weight, slant, set_width, spacing}. The font size can either be specified in units of pixels or points, and the appropriate metric is specified using the size _type argument. The x and y parameters together control the placement of the new text by specifying the upper left corner of the text bounding box. If the antialias parameter is non-zero, the generated text will blend more smoothly with underlying layer s. This option requires more time and memory to compute than non-antialiased text; the resulting floating selection or layer, however, will require the same amount of memory with or without antialiasing. If the specified drawable parameter is valid, the text will be created as a floating selection attached to the drawable. If the drawable parameter is not valid (-1), the text will appear as a new layer. Finally, a border can be specified around the final rendered text. The border is measured in pixel s.", "Spencer Kimball & Peter Mattis", "Spencer Kimball & Peter Mattis", "1995-1996", PDB_INTERNAL, /* Input arguments */ 15, text_tool_args, /* Output arguments */ 1, text_tool_out_args, /* Exec method */ { { text_tool_invoker } }, }; /**********************/ /* TEXT_GET_EXTENTS */ ProcArg text_tool_get_extents_args[] = { { PDB_STRING, "text", "the text to generate" }, { PDB_FLOAT, "size", "the size of text in either pixels or points" }, { PDB_INT32, "size_type", "the units of the specified size: { PIXELS (0), POINTS (1) }" }, { PDB_STRING, "foundry", "the font foundry, \"*\" for any" }, { PDB_STRING, "family", "the font family, \"*\" for any" }, { PDB_STRING, "weight", "the font weight, \"*\" for any" }, { PDB_STRING, "slant", "the font slant, \"*\" for any" }, { PDB_STRING, "set_width", "the font set-width parameter, \"*\" for any" }, { PDB_STRING, "spacing", "the font spacing, \"*\" for any" } }; ProcArg text_tool_get_extents_out_args[] = { { PDB_INT32, "width", "the width of the specified text" }, { PDB_INT32, "height", "the height of the specified text" }, { PDB_INT32, "ascent", "the ascent of the specified font" }, { PDB_INT32, "descent", "the descent of the specified font" } }; ProcRecord text_tool_get_extents_proc = { "gimp_text_get_extents", "Get extents of the bounding box for the specified text", "This tool returns the width and height of a bounding box for the specified text string with the specified font information. Ascent and descent for the specified font are returned as well.", "Spencer Kimball & Peter Mattis", "Spencer Kimball & Peter Mattis", "1995-1996", PDB_INTERNAL, /* Input arguments */ 9, text_tool_get_extents_args, /* Output arguments */ 4, text_tool_get_extents_out_args, /* Exec method */ { { text_tool_get_extents_invoker } }, }; static Argument * text_tool_invoker (Argument *args) { int success = TRUE; GImage *gimage; Layer *text_layer; int drawable_id; double x, y; char *text; int border; int antialias; double size; int size_type; char *foundry; char *family; char *weight; char *slant; char *set_width; char *spacing; int int_value; double fp_value; char fontname[2048]; Argument *return_args; text_layer = NULL; drawable_id = -1; x = 0; y = 0; text = NULL; border = FALSE; antialias = FALSE; size = 0; size_type = PIXELS; foundry = NULL; family = NULL; weight = NULL; slant = NULL; set_width = NULL; spacing = NULL; /* the gimage */ if (success) { int_value = args[0].value.pdb_int; if (! (gimage = gimage_get_ID (int_value))) success = FALSE; } /* the drawable */ if (success) { int_value = args[1].value.pdb_int; if (gimage == drawable_gimage (int_value)) drawable_id = int_value; else if (int_value == -1) drawable_id = -1; else success = FALSE; } /* x, y coordinates */ if (success) { x = args[2].value.pdb_float; y = args[3].value.pdb_float; } /* text */ if (success) text = (char *) args[4].value.pdb_pointer; /* border */ if (success) { int_value = args[5].value.pdb_int; if (int_value >= 0) border = int_value; else success = FALSE; } /* antialias */ if (success) { int_value = args[6].value.pdb_int; antialias = (int_value) ? TRUE : FALSE; } /* size */ if (success) { fp_value = args[7].value.pdb_float; if (fp_value > 0) size = fp_value; else success = FALSE; } /* size type */ if (success) { int_value = args[8].value.pdb_int; switch (int_value) { case 0: size_type = PIXELS; break; case 1: size_type = POINTS; break; default: success = FALSE; } } /* foundry, family, weight, slant, set_width, spacing */ if (success) { foundry = (char *) args[9].value.pdb_pointer; family = (char *) args[10].value.pdb_pointer; weight = (char *) args[11].value.pdb_pointer; slant = (char *) args[12].value.pdb_pointer; set_width = (char *) args[13].value.pdb_pointer; spacing = (char *) args[14].value.pdb_pointer; } /* increase the size by SUPERSAMPLE if we're antialiasing */ if (antialias) size *= SUPERSAMPLE; /* attempt to get the xlfd for the font */ if (success) success = text_get_xlfd (size, size_type, foundry, family, weight, slant, set_width, spacing, fontname); /* call the text render procedure */ if (success) success = ((text_layer = text_render (gimage, drawable_id, x, y, fontname, text, border, antialias)) != NULL); return_args = procedural_db_return_args (&text_tool_proc, success); if (success) return_args[1].value.pdb_int = text_layer->ID; return return_args; } static Argument * text_tool_get_extents_invoker (Argument *args) { int success = TRUE; char *text; double size; int size_type; char *foundry; char *family; char *weight; char *slant; char *set_width; char *spacing; int width, height; int ascent, descent; int int_value; double fp_value; char fontname[2048]; Argument *return_args; size = 0.0; size_type = PIXELS; /* text */ if (success) text = (char *) args[0].value.pdb_pointer; /* size */ if (success) { fp_value = args[1].value.pdb_float; if (fp_value > 0) size = fp_value; else success = FALSE; } /* size type */ if (success) { int_value = args[2].value.pdb_int; switch (int_value) { case 0: size_type = PIXELS; break; case 1: size_type = POINTS; break; default: success = FALSE; } } /* foundry, family, weight, slant, set_width, spacing */ if (success) { foundry = (char *) args[3].value.pdb_pointer; family = (char *) args[4].value.pdb_pointer; weight = (char *) args[5].value.pdb_pointer; slant = (char *) args[6].value.pdb_pointer; set_width = (char *) args[7].value.pdb_pointer; spacing = (char *) args[8].value.pdb_pointer; } /* attempt to get the xlfd for the font */ if (success) success = text_get_xlfd (size, size_type, foundry, family, weight, slant, set_width, spacing, fontname); /* call the text render procedure */ if (success) success = text_get_extents (fontname, text, &width, &height, &ascent, &descent); return_args = procedural_db_return_args (&text_tool_get_extents_proc, success); if (success) { return_args[1].value.pdb_int = width; return_args[2].value.pdb_int = height; return_args[3].value.pdb_int = ascent; return_args[4].value.pdb_int = descent; } return return_args; }