/* * GIMP Dynamic Text -- This is a plug-in for The GIMP 1.0 * Copyright (C) 1998,1999,2000 Marco Lamberto * Web page: http://www.geocities.com/Tokyo/1474/gimp/ * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ #include "config.h" #include #include #include #include #include "libgimp/gimp.h" #include "libgimp/stdplugins-intl.h" #include "gdyntext.h" #include "font_selection.h" static void gdt_query (void); static void gdt_run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); GimpPlugInInfo PLUG_IN_INFO = { NULL, NULL, gdt_query, gdt_run }; GdtVals gdtvals; #ifndef DEBUG_UI MAIN() #endif static void gdt_query (void) { static GimpParamDef gdt_args[] = { /* standard params */ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, { GIMP_PDB_IMAGE, "image", "Input image" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }, /* gdyntext params */ { GIMP_PDB_STRING, "text", "Text to render" }, { GIMP_PDB_INT32, "antialias", "Generate antialiased text" }, { GIMP_PDB_INT32, "alignment", "Text alignment: { LEFT = 0, CENTER = 1, RIGHT = 2 }" }, { GIMP_PDB_INT32, "rotation", "Text rotation (degrees)" }, { GIMP_PDB_INT32, "line_spacing", "Line spacing" }, { GIMP_PDB_COLOR, "color", "Text color" }, { GIMP_PDB_INT32, "layer_alignment", "Layer alignment { NONE = 0, BOTTOM_LEFT = 1, BOTTOM_CENTER = 2, BOTTOM_RIGHT = 3, MIDDLE_LEFT = 4, CENTER = 5, MIDDLE_RIGHT = 6, TOP_LEFT = 7, TOP_CENTER = 8, TOP_RIGHT = 9 }" }, { GIMP_PDB_STRING, "fontname", "The fontname (conforming to the X Logical Font Description Conventions)" } }; static GimpParamDef gdt_rets[] = { { GIMP_PDB_LAYER, "layer", "The text layer" } }; gimp_install_procedure ("plug_in_dynamic_text", "GIMP Dynamic Text", "", "Marco Lamberto ", "Marco Lamberto", "Jan 1999", N_("/Filters/Text/Dynamic Text..."), "RGB*,GRAY*,INDEXED*", GIMP_PLUGIN, G_N_ELEMENTS (gdt_args), G_N_ELEMENTS (gdt_rets), gdt_args, gdt_rets); } static void gdt_run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[2]; GimpRunMode run_mode; GdtVals oldvals; INIT_I18N_UI(); gdtvals.valid = TRUE; gdtvals.change_layer_name = FALSE; run_mode = param[0].data.d_int32; gdtvals.image_id = param[1].data.d_image; gdtvals.drawable_id = param[2].data.d_drawable; gdtvals.layer_id = param[2].data.d_layer; gdtvals.messages = NULL; gdtvals.preview = TRUE; *nreturn_vals = 2; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = GIMP_PDB_SUCCESS; values[1].type = GIMP_PDB_LAYER; values[1].data.d_int32 = -1; switch(run_mode) { case GIMP_RUN_INTERACTIVE: memset (&oldvals, 0, sizeof(GdtVals)); gimp_get_data ("plug_in_gdyntext", &oldvals); if (oldvals.valid) { strncpy (gdtvals.text, oldvals.text, sizeof(gdtvals.text)); strncpy (gdtvals.xlfd, oldvals.xlfd, sizeof(gdtvals.xlfd)); gdtvals.color = oldvals.color; gdtvals.antialias = oldvals.antialias; gdtvals.alignment = oldvals.alignment; gdtvals.rotation = oldvals.rotation; gdtvals.line_spacing = oldvals.line_spacing; gdtvals.layer_alignment = oldvals.layer_alignment; gdtvals.preview = oldvals.preview; } else { gdtvals.valid = FALSE; } gdt_load (&gdtvals); if (!gdt_create_ui (&gdtvals)) return; break; case GIMP_RUN_NONINTERACTIVE: #ifdef DEBUG g_print ("%d\n", nparams); #endif if (nparams != 11) { values[0].data.d_status = GIMP_PDB_CALLING_ERROR; return; } else { gdtvals.new_layer = !gimp_drawable_has_alpha (gdtvals.drawable_id); strncpy(gdtvals.text, param[3].data.d_string, sizeof(gdtvals.text)); gdtvals.antialias = param[4].data.d_int32; gdtvals.alignment = param[5].data.d_int32; gdtvals.rotation = param[6].data.d_int32; gdtvals.line_spacing = param[7].data.d_int32; gdtvals.color = param[8].data.d_color; gdtvals.layer_alignment = param[9].data.d_int32; strncpy(gdtvals.xlfd, param[10].data.d_string, sizeof(gdtvals.xlfd)); } break; case GIMP_RUN_WITH_LAST_VALS: gimp_get_data ("plug_in_gdyntext", &gdtvals); gdtvals.image_id = param[1].data.d_image; gdtvals.drawable_id = param[2].data.d_drawable; gdtvals.layer_id = param[2].data.d_layer; gdtvals.new_layer = !gimp_drawable_has_alpha (gdtvals.drawable_id); break; } gdt_render_text (&gdtvals); if (run_mode == GIMP_RUN_INTERACTIVE) { gdtvals.valid = TRUE; gimp_set_data ("plug_in_gdyntext", &gdtvals, sizeof(GdtVals)); } values[1].data.d_int32 = gdtvals.layer_id; } void gdt_load (GdtVals *data) { gint32 color; gchar *gdtparams = NULL; gchar *gdtparams0 = NULL; gchar **params = NULL; GimpParasite *parasite = NULL; if (gdt_compat_load (data)) return; if ((parasite = gimp_drawable_parasite_find (data->drawable_id, GDYNTEXT_PARASITE)) != NULL) { gdtparams = g_strdup (gimp_parasite_data (parasite)); gimp_parasite_free (parasite); } if (gdtparams == NULL) gdtparams = gimp_layer_get_name (data->layer_id); if (!gimp_drawable_has_alpha (data->drawable_id) || strncmp (gdtparams, "GDT", 3) != 0) { data->messages = g_list_append (data->messages, _(" Current layer isn't a GDynText layer or it has no alpha channel." " Forcing new layer creation.")); data->new_layer = TRUE; if (!data->valid) /* setup default values if it wasn't already done */ { strcpy (data->text, ""); strcpy (data->xlfd, ""); gimp_palette_get_foreground (&data->color); data->antialias = TRUE; data->alignment = LEFT; data->rotation = 0; data->line_spacing = 0; data->layer_alignment = LA_NONE; data->change_layer_name = FALSE; data->messages = NULL; data->preview = TRUE; } return; } #ifdef DEBUG g_print ("'%s'\n", gdtparams); #endif { gchar *st; gint len; st = strchr (gdtparams, '{') + 1; len = strrchr (st, '}') - st; gdtparams0 = g_strndup (st, len); } #ifdef DEBUG g_print ("'%s'\n", gdtparams0); #endif params = g_strsplit (gdtparams0, "}{", -1); g_free (gdtparams0); data->new_layer = FALSE; data->antialias = atoi (params[ANTIALIAS]); data->alignment = atoi (params[ALIGNMENT]); data->rotation = atoi (params[ROTATION]); data->line_spacing = atoi (params[LINE_SPACING]); data->layer_alignment = atoi (params[LAYER_ALIGNMENT]); color = strtol (params[COLOR], (gchar **)NULL, 16); gimp_rgb_set_uchar (&data->color, color >> 16, color >> 8, color); strncpy (data->xlfd, params[XLFD], sizeof(data->xlfd)); strncpy (data->text, params[TEXT], sizeof(data->text)); { gchar *text = g_strcompress (data->text); g_snprintf (data->text, sizeof(data->text), "%s", text); g_free (text); } g_free (gdtparams); g_strfreev (params); } void gdt_save (GdtVals *data) { gchar *lname; gchar *text; guchar r, g, b; gint32 color; GimpParasite *parasite; text = g_strescape (data->text, NULL); gimp_rgb_get_uchar (&data->color, &r, &g, &b); color = (r << 16) | (g << 8) | b; lname = g_strdup_printf (GDYNTEXT_MAGIC "{%s}{%d}{%d}{%d}{%d}{%06X}{%d}{%s}", text, data->antialias, data->alignment, data->rotation, data->line_spacing, color, data->layer_alignment, data->xlfd); g_free(text); parasite = gimp_parasite_new (GDYNTEXT_PARASITE, GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, strlen(lname), lname); gimp_drawable_parasite_attach (data->drawable_id, parasite); gimp_parasite_free (parasite); /* change the layer name as in GIMP 1.x */ if (data->change_layer_name) gimp_layer_set_name (data->layer_id, _("GDynText Layer")); gimp_displays_flush (); g_free (lname); } void gdt_render_text (GdtVals *data) { gdt_render_text_p (data, TRUE); } void gdt_render_text_p (GdtVals *data, gboolean show_progress) { gint layer_ox, layer_oy, i, xoffs; gint32 layer_f, selection_empty, selection_channel = -1; gint32 text_width, text_height = 0; gint32 text_ascent, text_descent; gint32 layer_width, layer_height; gint32 image_width, image_height; gint32 space_width; gfloat font_size; gint32 font_size_type; gchar **text_xlfd, **text_lines; gint32 *text_lines_w; GimpRGB old_color; if (show_progress) gimp_progress_init (_("GIMP Dynamic Text")); /* undo start */ gimp_undo_push_group_start (gdtvals.image_id); /* save and remove current selection */ selection_empty = gimp_selection_is_empty (data->image_id); if (!selection_empty) { /* there is an active selection to save */ selection_channel = gimp_selection_save (data->image_id); gimp_selection_none (data->image_id); } text_xlfd = g_strsplit (data->xlfd, "-", -1); if (!strlen (text_xlfd[XLFD_PIXEL_SIZE]) || !strcmp (text_xlfd[XLFD_PIXEL_SIZE], "*")) { font_size = atof (text_xlfd[XLFD_POINT_SIZE]); font_size_type = FONT_METRIC_POINTS; } else { font_size = atof (text_xlfd[XLFD_PIXEL_SIZE]); font_size_type = FONT_METRIC_PIXELS; } g_strfreev (text_xlfd); /* retrieve space char width */ gimp_text_get_extents_fontname ("AA", font_size, font_size_type, data->xlfd, &text_width, &text_height, &text_ascent, &text_descent); space_width = text_width; gimp_text_get_extents_fontname ("AA", font_size, font_size_type, data->xlfd, &text_width, &text_height, &text_ascent, &text_descent); space_width -= text_width; #ifdef DEBUG g_print ("GDT: space width = %d\n", space_width); #endif /* setup text and compute layer size */ text_lines = g_strsplit (data->text, "\n", -1); for (i = 0; text_lines[i]; i++); text_lines_w = g_new0 (gint32, i); layer_width = layer_height = 0; for (i = 0; text_lines[i]; i++) { gimp_text_get_extents_fontname (strlen(text_lines[i]) > 0 ? text_lines[i] : " ", font_size, font_size_type, data->xlfd, &text_width, &text_height, &text_ascent, &text_descent); #ifdef DEBUG g_print ("GDT: %4dx%4d A:%3d D:%3d [%s]\n", text_width, text_height, text_ascent, text_descent, text_lines[i]); #endif text_lines_w[i] = text_width; if (layer_width < text_width) layer_width = text_width; layer_height += text_height + data->line_spacing; } layer_height -= data->line_spacing; if (layer_height == 0) layer_height = 10; if (layer_width == 0) layer_width = 10; if (!data->new_layer) { /* resize the old layer */ gimp_layer_resize (data->layer_id, layer_width, layer_height, 0, 0); } else { /* create a new layer */ data->layer_id = data->drawable_id = gimp_layer_new (data->image_id, _("GDynText Layer"), layer_width, layer_height, (GimpImageType)(gimp_image_base_type (data->image_id) * 2 + 1), 100.0, GIMP_NORMAL_MODE); gimp_layer_add_alpha (data->layer_id); gimp_image_add_layer (data->image_id, data->layer_id, 0); gimp_image_set_active_layer (data->image_id, data->layer_id); } /* clear layer */ gimp_layer_set_preserve_transparency (data->layer_id, 0); gimp_edit_clear (data->drawable_id); /* get layer offsets */ gimp_drawable_offsets (data->layer_id, &layer_ox, &layer_oy); /* get foreground color */ gimp_palette_get_foreground (&old_color); /* set foreground color to the wanted text color */ gimp_palette_set_foreground (&data->color); /* write text */ for (i = 0; text_lines[i]; i++) { switch (data->alignment) { case LEFT: xoffs = 0; break; case RIGHT: xoffs = layer_width - text_lines_w[i]; break; case CENTER: xoffs = (layer_width - text_lines_w[i]) / 2; break; default: xoffs = 0; } layer_f = gimp_text_fontname (data->image_id, data->drawable_id, (gdouble)layer_ox + strspn (text_lines[i], " ") * space_width + xoffs, /* x */ (gdouble)layer_oy + i * (text_height + data->line_spacing), /* y */ text_lines[i], -1, /* border */ data->antialias, font_size, font_size_type, data->xlfd); /* FIXME: ascent/descent stuff, use gimp_layer_translate */ #ifdef DEBUG g_print ("GDT: MH:%d LH:%d\n", text_height, gimp_drawable_height (layer_f)); #endif gimp_floating_sel_anchor (layer_f); if (show_progress) gimp_progress_update ((gdouble)(i + 2) * 100.0 * (gdouble)text_height / (gdouble)layer_height); } g_strfreev (text_lines); g_free (text_lines_w); /* set foreground color to the old one */ gimp_palette_set_foreground (&old_color); /* apply rotation */ if (data->rotation != 0 && abs(data->rotation) != 360) { gimp_rotate (data->drawable_id, TRUE, (gdouble)data->rotation * G_PI / 180.0); gimp_layer_set_offsets (data->layer_id, layer_ox, layer_oy); } /* sets the layer alignment */ layer_width = gimp_drawable_width (data->drawable_id); layer_height = gimp_drawable_height (data->drawable_id); image_width = gimp_image_width (data->image_id); image_height = gimp_image_height (data->image_id); switch (data->layer_alignment) { case LA_TOP_LEFT: gimp_layer_set_offsets(data->layer_id, 0, 0); break; case LA_TOP_CENTER: gimp_layer_set_offsets(data->layer_id, (image_width - layer_width) >> 1, 0); break; case LA_TOP_RIGHT: gimp_layer_set_offsets(data->layer_id, image_width - layer_width, 0); break; case LA_MIDDLE_LEFT: gimp_layer_set_offsets(data->layer_id, 0, (image_height - layer_height) >> 1); break; case LA_CENTER: gimp_layer_set_offsets(data->layer_id, (image_width - layer_width) >> 1, (image_height - layer_height) >> 1); break; case LA_MIDDLE_RIGHT: gimp_layer_set_offsets(data->layer_id, image_width - layer_width, (image_height - layer_height) >> 1); break; case LA_BOTTOM_LEFT: gimp_layer_set_offsets(data->layer_id, 0, image_height - layer_height); break; case LA_BOTTOM_CENTER: gimp_layer_set_offsets(data->layer_id, (image_width - layer_width) >> 1, image_height - layer_height); break; case LA_BOTTOM_RIGHT: gimp_layer_set_offsets(data->layer_id, image_width - layer_width, image_height - layer_height); break; case LA_NONE: default: break; } gimp_layer_set_preserve_transparency (data->layer_id, 1); /* restore old selection if any */ if (selection_empty == FALSE) { gimp_selection_load (selection_channel); gimp_image_remove_channel (data->image_id, selection_channel); } gdt_save (data); /* undo end */ gimp_undo_push_group_end (gdtvals.image_id); if (show_progress) gimp_progress_update (100.0); gimp_displays_flush (); }