home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / text_tool.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-17  |  27.8 KB  |  1,049 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <stdlib.h>
  22. #include <string.h>
  23.  
  24. #include <gdk/gdkkeysyms.h>
  25. #include <gdk/gdk.h>
  26.  
  27. #ifndef GDK_WINDOWING_WIN32
  28. #include <gdk/gdkx.h>
  29. #endif
  30.  
  31. #include <gdk/gdkprivate.h>
  32.  
  33. #include "apptypes.h"
  34.  
  35. #include "appenv.h"
  36. #include "drawable.h"
  37. #include "edit_selection.h"
  38. #include "errors.h"
  39. #include "floating_sel.h"
  40. #include "gimage_mask.h"
  41. #include "gimpui.h"
  42. #include "global_edit.h"
  43. #include "procedural_db.h"
  44. #include "selection.h"
  45. #include "text_tool.h"
  46. #include "tools.h"
  47. #include "undo.h"
  48.  
  49. #include "tile_manager_pvt.h"
  50. #include "drawable_pvt.h"
  51.  
  52. #include "config.h"
  53. #include "libgimp/gimplimits.h"
  54. #include "libgimp/gimpintl.h"
  55.  
  56. #define FOUNDRY      0
  57. #define FAMILY       1
  58. #define WEIGHT       2
  59. #define SLANT        3
  60. #define SET_WIDTH    4
  61. #define PIXEL_SIZE   6
  62. #define POINT_SIZE   7
  63. #define XRESOLUTION  8
  64. #define YRESOLUTION  9
  65. #define SPACING     10
  66. #define REGISTRY    12
  67. #define ENCODING    13
  68.  
  69. /*  the text tool structures  */
  70.  
  71. typedef struct _TextTool TextTool;
  72.  
  73. struct _TextTool
  74. {
  75.   gint  click_x;
  76.   gint  click_y;
  77.   void *gdisp_ptr;
  78. };
  79.  
  80. typedef struct _TextOptions TextOptions;
  81.  
  82. struct _TextOptions
  83. {
  84.   ToolOptions  tool_options;
  85.  
  86.   gboolean     antialias;
  87.   gboolean     antialias_d;
  88.   GtkWidget   *antialias_w;
  89.  
  90.   gint         border;
  91.   gint         border_d;
  92.   GtkObject   *border_w;
  93.  
  94.   gboolean     use_dyntext;
  95.   gboolean     use_dyntext_d;
  96.   GtkWidget   *use_dyntext_w;
  97. };
  98.  
  99.  
  100. /*  the text tool options  */
  101. static TextOptions *text_options = NULL;
  102.  
  103. /*  local variables  */
  104. static TextTool  *the_text_tool   = NULL;
  105. static GtkWidget *text_tool_shell = NULL;
  106.  
  107.  
  108. static void   text_button_press   (Tool *, GdkEventButton *, gpointer);
  109. static void   text_button_release (Tool *, GdkEventButton *, gpointer);
  110. static void   text_cursor_update  (Tool *, GdkEventMotion *, gpointer);
  111. static void   text_control        (Tool *, ToolAction,       gpointer);
  112.  
  113. static void   text_dialog_create          (void);
  114. static void   text_dialog_ok_callback     (GtkWidget *, gpointer);
  115. static void   text_dialog_cancel_callback (GtkWidget *, gpointer);
  116. static gint   text_dialog_delete_callback (GtkWidget *, GdkEvent *, gpointer);
  117.  
  118. static void   text_init_render         (TextTool *);
  119. static void   text_gdk_image_to_region (GdkImage *, gint, PixelRegion *);
  120. static void   text_size_multiply       (gchar **fontname, gint);
  121. static void   text_set_resolution      (gchar **fontname, gdouble, gdouble);
  122.  
  123. /*  Layer       * text_render (GImage *, GimpDrawable *, */
  124. /*                 gint, gint, gchar *, gchar *, gint, gint); */
  125.  
  126.  
  127. /*  functions  */
  128.  
  129. static void
  130. text_options_reset (void)
  131. {
  132.   TextOptions *options = text_options;
  133.  
  134.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->antialias_w),
  135.                 options->antialias_d);
  136.   gtk_adjustment_set_value (GTK_ADJUSTMENT (options->border_w),
  137.                 options->border_d);
  138.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->use_dyntext_w),
  139.                 options->use_dyntext_d);
  140. }
  141.  
  142. static TextOptions *
  143. text_options_new (void)
  144. {
  145.   TextOptions *options;
  146.  
  147.   GtkWidget *vbox;
  148.   GtkWidget *hbox;
  149.   GtkWidget *label;
  150.   GtkWidget *spinbutton;
  151.   GtkWidget *sep;
  152.  
  153.   /*  the new text tool options structure  */
  154.   options = g_new (TextOptions, 1);
  155.   tool_options_init ((ToolOptions *) options,
  156.              _("Text Tool"),
  157.              text_options_reset);
  158.   options->antialias   = options->antialias_d   = TRUE;
  159.   options->border      = options->border_d      = 0;
  160.   options->use_dyntext = options->use_dyntext_d = FALSE;
  161.  
  162.   /*  the main vbox  */
  163.   vbox = options->tool_options.main_vbox;
  164.  
  165.   /*  antialias toggle  */
  166.   options->antialias_w =
  167.     gtk_check_button_new_with_label (_("Antialiasing"));
  168.   gtk_signal_connect (GTK_OBJECT (options->antialias_w), "toggled",
  169.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  170.               &options->antialias);
  171.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->antialias_w),
  172.                 options->antialias_d);
  173.   gtk_box_pack_start (GTK_BOX (vbox), options->antialias_w,
  174.               FALSE, FALSE, 0);
  175.   gtk_widget_show (options->antialias_w);
  176.  
  177.   /*  the border spinbutton  */
  178.   hbox = gtk_hbox_new (FALSE, 4);
  179.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  180.  
  181.   label = gtk_label_new (_("Border:"));
  182.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  183.   gtk_widget_show (label);
  184.  
  185.   options->border_w =
  186.     gtk_adjustment_new (options->border_d, 0.0, 32767.0, 1.0, 50.0, 0.0);
  187.   gtk_signal_connect (GTK_OBJECT (options->border_w), "value_changed",
  188.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  189.               &options->border);
  190.   spinbutton =
  191.     gtk_spin_button_new (GTK_ADJUSTMENT (options->border_w), 1.0, 0.0);
  192.   gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
  193.                    GTK_SHADOW_NONE);
  194.   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
  195.   gtk_widget_set_usize (spinbutton, 75, 0);
  196.   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
  197.   gtk_widget_show (spinbutton);
  198.  
  199.   gtk_widget_show (hbox);
  200.  
  201.   sep = gtk_hseparator_new ();
  202.   gtk_box_pack_start (GTK_BOX (vbox), sep, FALSE, FALSE, 0);
  203.   gtk_widget_show (sep);
  204.  
  205.   /*  the dynamic text toggle  */
  206.   options->use_dyntext_w =
  207.     gtk_check_button_new_with_label (_("Use Dynamic Text"));
  208.   gtk_signal_connect (GTK_OBJECT (options->use_dyntext_w), "toggled",
  209.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  210.               &options->use_dyntext);
  211.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->use_dyntext_w),
  212.                 options->use_dyntext_d);
  213.   gtk_box_pack_start (GTK_BOX (vbox), options->use_dyntext_w,
  214.               FALSE, FALSE, 0);
  215.   gtk_widget_show (options->use_dyntext_w);
  216.  
  217.   /*  let the toggle callback set the sensitive states  */
  218.   gtk_widget_set_sensitive (options->antialias_w, ! options->use_dyntext_d);
  219.   gtk_widget_set_sensitive (spinbutton, ! options->use_dyntext_d);
  220.   gtk_widget_set_sensitive (label, ! options->use_dyntext_d);
  221.   gtk_object_set_data (GTK_OBJECT (options->use_dyntext_w), "inverse_sensitive",
  222.                spinbutton);
  223.   gtk_object_set_data (GTK_OBJECT (spinbutton), "inverse_sensitive", label);
  224.   gtk_object_set_data (GTK_OBJECT (label), "inverse_sensitive",
  225.                options->antialias_w);
  226.  
  227.   return options;
  228. }
  229.  
  230. Tool*
  231. tools_new_text (void)
  232. {
  233.   Tool * tool;
  234.  
  235.   /*  The tool options  */
  236.   if (! text_options)
  237.     {
  238.       text_options = text_options_new ();
  239.       tools_register (TEXT, (ToolOptions *) text_options);
  240.     }
  241.  
  242.   /*  the new text tool structure  */
  243.   tool = tools_new_tool (TEXT);
  244.   the_text_tool = g_new (TextTool, 1);
  245.  
  246.   tool->scroll_lock = TRUE;  /* Disallow scrolling */
  247.  
  248.   tool->private = (void *) the_text_tool;
  249.  
  250.   tool->button_press_func   = text_button_press;
  251.   tool->button_release_func = text_button_release;
  252.   tool->cursor_update_func  = text_cursor_update;
  253.   tool->control_func        = text_control;
  254.  
  255.   return tool;
  256. }
  257.  
  258. void
  259. tools_free_text (Tool *tool)
  260. {
  261.   g_free (tool->private);
  262.   the_text_tool = NULL;
  263.  
  264.   if (text_tool_shell)
  265.     gimp_dialog_hide (text_tool_shell);
  266. }
  267.  
  268. static void
  269. text_call_gdyntext (GDisplay *gdisp)
  270. {
  271.   ProcRecord *proc_rec;
  272.   Argument   *args;
  273.  
  274.   /*  find the gDynText PDB record  */
  275.   if ((proc_rec = procedural_db_lookup ("plug_in_dynamic_text")) == NULL)
  276.     {
  277.       g_message ("text_call_gdyntext: gDynText procedure lookup failed");
  278.       return;
  279.     }
  280.  
  281.   /*  plug-in arguments as if called by <Image>/Filters/...  */
  282.   args = g_new (Argument, 3);
  283.   args[0].arg_type = PDB_INT32;
  284.   args[0].value.pdb_int = RUN_INTERACTIVE;
  285.   args[1].arg_type = PDB_IMAGE;
  286.   args[1].value.pdb_int = (gint32) pdb_image_to_id (gdisp->gimage);
  287.   args[2].arg_type = PDB_DRAWABLE;
  288.   args[2].value.pdb_int = (gint32) gimage_active_drawable (gdisp->gimage)->ID;
  289.  
  290.   plug_in_run (proc_rec, args, 3, FALSE, TRUE, gdisp->ID);
  291.  
  292.   g_free (args);
  293. }
  294.  
  295. static void
  296. text_button_press (Tool           *tool,
  297.            GdkEventButton *bevent,
  298.            gpointer        gdisp_ptr)
  299. {
  300.   GDisplay *gdisp;
  301.   Layer *layer;
  302.   TextTool *text_tool;
  303.  
  304.   gdisp = gdisp_ptr;
  305.  
  306.   text_tool = tool->private;
  307.   text_tool->gdisp_ptr = gdisp_ptr;
  308.  
  309.   tool->state = ACTIVE;
  310.   tool->gdisp_ptr = gdisp_ptr;
  311.  
  312.   gdisplay_untransform_coords (gdisp, bevent->x, bevent->y,
  313.                    &text_tool->click_x, &text_tool->click_y,
  314.                    TRUE, 0);
  315.  
  316.   if ((layer = gimage_pick_correlate_layer (gdisp->gimage,
  317.                         text_tool->click_x,
  318.                         text_tool->click_y)))
  319.     /*  If there is a floating selection, and this aint it, use the move tool  */
  320.     if (layer_is_floating_sel (layer))
  321.       {
  322.     init_edit_selection (tool, gdisp_ptr, bevent, EDIT_LAYER_TRANSLATE);
  323.     return;
  324.       }
  325.  
  326.   if (text_options->use_dyntext)
  327.     {
  328.       text_call_gdyntext (gdisp);
  329.       return;
  330.     }
  331.  
  332.   if (! text_tool_shell)
  333.     text_dialog_create ();
  334.  
  335.   if (!GTK_WIDGET_VISIBLE (text_tool_shell))
  336.     gtk_widget_show (text_tool_shell);
  337. }
  338.  
  339. static void
  340. text_button_release (Tool           *tool,
  341.              GdkEventButton *bevent,
  342.              gpointer        gdisp_ptr)
  343. {
  344.   tool->state = INACTIVE;
  345. }
  346.  
  347. static void
  348. text_cursor_update (Tool           *tool,
  349.             GdkEventMotion *mevent,
  350.             gpointer        gdisp_ptr)
  351. {
  352.   GDisplay *gdisp;
  353.   Layer *layer;
  354.   gint x, y;
  355.  
  356.   gdisp = (GDisplay *) gdisp_ptr;
  357.  
  358.   gdisplay_untransform_coords (gdisp, mevent->x, mevent->y,
  359.                    &x, &y, FALSE, FALSE);
  360.  
  361.   if ((layer = gimage_pick_correlate_layer (gdisp->gimage, x, y)))
  362.     /*  if there is a floating selection, and this aint it...  */
  363.     if (layer_is_floating_sel (layer))
  364.       {
  365.     gdisplay_install_tool_cursor (gdisp, GDK_FLEUR,
  366.                       MOVE,
  367.                       CURSOR_MODIFIER_NONE,
  368.                       FALSE);
  369.     return;
  370.       }
  371.  
  372.   gdisplay_install_tool_cursor (gdisp, GDK_XTERM,
  373.                 TEXT,
  374.                 CURSOR_MODIFIER_NONE,
  375.                 FALSE);
  376. }
  377.  
  378. static void
  379. text_control (Tool       *tool,
  380.           ToolAction  action,
  381.           gpointer    gdisp_ptr)
  382. {
  383.   switch (action)
  384.     {
  385.     case PAUSE:
  386.       break;
  387.  
  388.     case RESUME:
  389.       break;
  390.  
  391.     case HALT:
  392.       if (text_tool_shell)
  393.     gimp_dialog_hide (text_tool_shell);
  394.       break;
  395.  
  396.     default:
  397.       break;
  398.     }
  399. }
  400.  
  401. static void
  402. text_dialog_create (void)
  403. {
  404.   /* Create the shell */
  405.   text_tool_shell = gtk_font_selection_dialog_new (_("Text Tool"));
  406.   gtk_window_set_wmclass (GTK_WINDOW (text_tool_shell), "text_tool", "Gimp");
  407.   gtk_window_set_policy (GTK_WINDOW (text_tool_shell), FALSE, TRUE, FALSE);
  408.   gtk_window_set_position (GTK_WINDOW (text_tool_shell), GTK_WIN_POS_MOUSE);
  409.  
  410.   /* handle the wm close signal */
  411.   gtk_signal_connect (GTK_OBJECT (text_tool_shell), "delete_event",
  412.               GTK_SIGNAL_FUNC (text_dialog_delete_callback),
  413.               NULL);
  414.  
  415.   /* ok and cancel buttons */
  416.   gtk_signal_connect (GTK_OBJECT (GTK_FONT_SELECTION_DIALOG
  417.                   (text_tool_shell)->ok_button), "clicked",
  418.               GTK_SIGNAL_FUNC (text_dialog_ok_callback),
  419.               NULL);
  420.  
  421.   gtk_signal_connect (GTK_OBJECT (GTK_FONT_SELECTION_DIALOG
  422.                   (text_tool_shell)->cancel_button), "clicked",
  423.               GTK_SIGNAL_FUNC (text_dialog_cancel_callback),
  424.               NULL);
  425.  
  426.   /* Show the shell */
  427.   gtk_widget_show (text_tool_shell);
  428. }
  429.  
  430. static void
  431. text_dialog_ok_callback (GtkWidget *widget,
  432.              gpointer   data)
  433. {
  434.   gimp_dialog_hide (text_tool_shell);
  435.  
  436.   if (the_text_tool)
  437.     text_init_render (the_text_tool);
  438. }
  439.  
  440. static gint
  441. text_dialog_delete_callback (GtkWidget *widget,
  442.                  GdkEvent  *event,
  443.                  gpointer   data)
  444. {
  445.   text_dialog_cancel_callback (widget, data);
  446.   
  447.   return TRUE;
  448. }
  449.  
  450. static void
  451. text_dialog_cancel_callback (GtkWidget *widget,
  452.                  gpointer   data)
  453. {
  454.   gimp_dialog_hide (text_tool_shell);
  455. }
  456.  
  457. static void
  458. text_init_render (TextTool *text_tool)
  459. {
  460.   GDisplay *gdisp;
  461.   gchar *fontname;
  462.   gchar *text;
  463.   gboolean antialias = text_options->antialias;
  464.  
  465.   fontname = gtk_font_selection_dialog_get_font_name
  466.     (GTK_FONT_SELECTION_DIALOG (text_tool_shell));
  467.   if (!fontname)
  468.     return;
  469.  
  470.   gdisp = (GDisplay *) text_tool->gdisp_ptr;
  471.  
  472.   /* override the user's antialias setting if this is an indexed image */
  473.   if (gimage_base_type (gdisp->gimage) == INDEXED)
  474.     antialias = FALSE;
  475.  
  476.   /* If we're anti-aliasing, request a larger font than user specified.
  477.    * This will probably produce a font which isn't available if fonts
  478.    * are not scalable on this particular X server.  TODO: Ideally, should
  479.    * grey out anti-alias on these kinds of servers. */
  480.   if (antialias)
  481.     text_size_multiply (&fontname, SUPERSAMPLE);
  482.  
  483.   /*  If the text size is specified in points, it's size will be scaled
  484.    *  correctly according to the image's resolution.
  485.    *  FIXME: this currently can't be activated for the PDB, as the text has
  486.    *         to be rendered in the size "text_get_extents" returns.
  487.    *  TODO: add resolution parameters to "text_get_extents"
  488.    */
  489.   text_set_resolution (&fontname,
  490.                gdisp->gimage->xresolution,
  491.                gdisp->gimage->yresolution);
  492.  
  493.   text = gtk_font_selection_dialog_get_preview_text
  494.     (GTK_FONT_SELECTION_DIALOG (text_tool_shell));
  495.  
  496.   /* strdup it since the render function strtok()s the text */
  497.   text = g_strdup (text);
  498.  
  499.   text_render (gdisp->gimage, gimage_active_drawable (gdisp->gimage),
  500.            text_tool->click_x, text_tool->click_y,
  501.            fontname, text, text_options->border, antialias);
  502.  
  503.   gdisplays_flush ();
  504.  
  505.   g_free (fontname);
  506.   g_free (text);
  507. }
  508.  
  509. static void
  510. text_gdk_image_to_region (GdkImage    *image,
  511.               gint         scale,
  512.               PixelRegion *textPR)
  513. {
  514.   GdkColor black;
  515.   gint black_pixel;
  516.   gint pixel;
  517.   gint value;
  518.   gint scalex, scaley;
  519.   gint scale2;
  520.   gint x, y;
  521.   gint i, j;
  522.   guchar * data;
  523.  
  524.   scale2 = scale * scale;
  525. /* GDK_WINDOWING is defined only with GTk+ 1.3 */
  526. #ifndef GDK_WINDOWING_WIN32
  527.   black.red = black.green = black.blue = 0;
  528.   gdk_colormap_alloc_color (gdk_colormap_get_system (), &black, FALSE, TRUE);
  529.   black_pixel = black.pixel;
  530. #else
  531.   black_pixel = 0;
  532. #endif
  533.   data = textPR->data;
  534.  
  535.   for (y = 0, scaley = 0; y < textPR->h; y++, scaley += scale)
  536.     {
  537.       for (x = 0, scalex = 0; x < textPR->w; x++, scalex += scale)
  538.     {
  539.       value = 0;
  540.  
  541.       for (i = scaley; i < scaley + scale; i++)
  542.         for (j = scalex; j < scalex + scale; j++)
  543.           {
  544.         pixel = gdk_image_get_pixel (image, j, i);
  545.         if (pixel == black_pixel)
  546.           value ++;
  547.           }
  548.  
  549.       /*  store the alpha value in the data  */
  550.       *data++= (guchar) ((value * 255) / scale2);
  551.  
  552.     }
  553.     }
  554. }
  555.  
  556. GimpLayer *
  557. text_render (GimpImage    *gimage,
  558.          GimpDrawable *drawable,
  559.          gint          text_x,
  560.          gint          text_y,
  561.          gchar        *fontname,
  562.          gchar        *text,
  563.          gint          border,
  564.          gint          antialias)
  565. {
  566.   GdkFont *font;
  567.   GdkPixmap *pixmap;
  568.   GdkImage *image;
  569.   GdkGC *gc;
  570.   GdkColor black, white;
  571.   Layer *layer;
  572.   TileManager *mask, *newmask;
  573.   PixelRegion textPR, maskPR;
  574.   gint layer_type;
  575.   guchar color[MAX_CHANNELS];
  576.   gchar *str;
  577.   gint nstrs;
  578.   gboolean crop;
  579.   gint line_width, line_height;
  580.   gint pixmap_width, pixmap_height;
  581.   gint text_width, text_height;
  582.   gint width, height;
  583.   gint x, y, k;
  584.   void *pr;
  585. #ifndef GDK_WINDOWING_WIN32
  586.   XFontStruct *xfs;
  587. #endif
  588.  
  589.   /*  determine the layer type  */
  590.   if (drawable)
  591.     layer_type = drawable_type_with_alpha (drawable);
  592.   else
  593.     layer_type = gimage_base_type_with_alpha (gimage);
  594.  
  595.   /* scale the text based on the antialiasing amount */
  596.   if (antialias)
  597.     antialias = SUPERSAMPLE;
  598.   else
  599.     antialias = 1;
  600.  
  601.   /* Dont crop the text if border is negative */
  602.   crop = (border >= 0);
  603.   if (!crop) 
  604.     border = 0;
  605.  
  606.   /* load the font in */
  607.   gdk_error_warnings = 0;
  608.   gdk_error_code = 0;
  609. #ifndef GDK_WINDOWING_WIN32
  610.   font = gdk_font_load (fontname);
  611.   if (!font)
  612.     {
  613.       g_message (_("Font '%s' not found."), fontname);
  614.       return NULL;
  615.     }
  616.   xfs = GDK_FONT_XFONT (font);
  617.   if (xfs->min_byte1 != 0 || xfs->max_byte1 != 0) 
  618.     {
  619.       gchar *fname;
  620.  
  621.       gdk_font_unref (font);
  622.       fname = g_strdup_printf ("%s,*", fontname);
  623.       font = gdk_fontset_load (fname);
  624.       g_free (fname);
  625.     }
  626. #else
  627.   /* Just use gdk_fontset_load all the time. IMHO it could be like
  628.    * this on all platforms?
  629.    */
  630.   font = gdk_fontset_load (fontname);
  631. #endif
  632.   gdk_error_warnings = 1;
  633.   if (!font || (gdk_error_code == -1))
  634.     {
  635.       g_message (_("Font '%s' not found.%s"),
  636.          fontname,
  637.          antialias > 1 ?
  638.          _("\nIf you don't have scalable fonts, "
  639.            "try turning off antialiasing in the tool options.") : "");
  640.       return NULL;
  641.     }
  642.  
  643.   /* determine the bounding box of the text */
  644.   width = -1;
  645.   height = 0;
  646.   line_height = font->ascent + font->descent;
  647.  
  648.   nstrs = 0;
  649.   str = strtok (text, "\n");
  650.   while (str)
  651.     {
  652.       nstrs += 1;
  653.  
  654.       /* gdk_string_measure will give the correct width of the
  655.        *  string. However, we'll add a little "fudge" factor just
  656.        *  to be sure.
  657.        */
  658.       line_width = gdk_string_measure (font, str) + 5;
  659.       if (line_width > width)
  660.     width = line_width;
  661.       height += line_height;
  662.  
  663.       str = strtok (NULL, "\n");
  664.     }
  665.  
  666.   /* We limit the largest pixmap we create to approximately 200x200.
  667.    * This is approximate since it depends on the amount of antialiasing.
  668.    * Basically, we want the width and height to be divisible by the antialiasing
  669.    *  amount. (Which lies in the range 1-10).
  670.    * This avoids problems on some X-servers (Xinside) which have problems
  671.    *  with large pixmaps. (Specifically pixmaps which are larger - width
  672.    *  or height - than the screen).
  673.    */
  674.   pixmap_width = TILE_WIDTH * antialias;
  675.   pixmap_height = TILE_HEIGHT * antialias;
  676.  
  677.   /* determine the actual text size based on the amount of antialiasing */
  678.   text_width = width / antialias;
  679.   text_height = height / antialias;
  680.  
  681.   /* create the pixmap of depth 1 */
  682.   pixmap = gdk_pixmap_new (NULL, pixmap_width, pixmap_height, 1);
  683.  
  684.   /* create the gc */
  685.   gc = gdk_gc_new (pixmap);
  686.   gdk_gc_set_font (gc, font);
  687.  
  688.   /*  get black and white pixels for this gdisplay  */
  689.   black.red = black.green = black.blue = 0;
  690.   white.red = white.green = white.blue = 65535;
  691. #ifndef GDK_WINDOWING_WIN32
  692.   gdk_colormap_alloc_color (gdk_colormap_get_system (), &black, FALSE, TRUE);
  693.   gdk_colormap_alloc_color (gdk_colormap_get_system (), &white, FALSE, TRUE);
  694. #else
  695.   black.pixel = 0;
  696.   white.pixel = 1;
  697. #endif
  698.  
  699.   /* Render the text into the pixmap.
  700.    * Since the pixmap may not fully bound the text (because we limit its size)
  701.    *  we must tile it around the texts actual bounding box.
  702.    */
  703.   mask = tile_manager_new (text_width, text_height, 1);
  704.   pixel_region_init (&maskPR, mask, 0, 0, text_width, text_height, TRUE);
  705.  
  706.   for (pr = pixel_regions_register (1, &maskPR); 
  707.        pr != NULL; 
  708.        pr = pixel_regions_process (pr))
  709.     {
  710.       /* erase the pixmap */
  711.       gdk_gc_set_foreground (gc, &white);
  712.       gdk_draw_rectangle (pixmap, gc, 1, 0, 0, pixmap_width, pixmap_height);
  713.       gdk_gc_set_foreground (gc, &black);
  714.  
  715.       /* adjust the x and y values */
  716.       x = -maskPR.x * antialias;
  717.       y = font->ascent - maskPR.y * antialias;
  718.       str = text;
  719.  
  720.       for (k = 0; k < nstrs; k++)
  721.     {
  722.       gdk_draw_string (pixmap, font, gc, x, y, str);
  723.       str += strlen (str) + 1;
  724.       y += line_height;
  725.     }
  726.  
  727.       /* create the GdkImage */
  728.       image = gdk_image_get (pixmap, 0, 0, pixmap_width, pixmap_height);
  729.  
  730.       if (!image)
  731.     gimp_fatal_error ("text_render(): Sanity check failed: could not get gdk image");
  732.  
  733.       if (image->depth != 1)
  734.     gimp_fatal_error ("text_render(): Sanity check failed: image should have 1 bit per pixel");
  735.  
  736.       /* convert the GdkImage bitmap to a region */
  737.       text_gdk_image_to_region (image, antialias, &maskPR);
  738.  
  739.       /* free the image */
  740.       gdk_image_destroy (image);
  741.     }
  742.  
  743.   /*  Crop the mask buffer  */
  744.   newmask = crop ? crop_buffer (mask, border) : mask;
  745.   if (newmask != mask)
  746.     tile_manager_destroy (mask);
  747.  
  748.   if (newmask && 
  749.       (layer = layer_new (gimage, newmask->width,
  750.              newmask->height, layer_type,
  751.              _("Text Layer"), OPAQUE_OPACITY, NORMAL_MODE)))
  752.     {
  753.       /*  color the layer buffer  */
  754.       gimage_get_foreground (gimage, drawable, color);
  755.       color[GIMP_DRAWABLE (layer)->bytes - 1] = OPAQUE_OPACITY;
  756.       pixel_region_init (&textPR, GIMP_DRAWABLE (layer)->tiles,
  757.              0, 0,
  758.              GIMP_DRAWABLE (layer)->width,
  759.              GIMP_DRAWABLE (layer)->height, TRUE);
  760.       color_region (&textPR, color);
  761.  
  762.       /*  apply the text mask  */
  763.       pixel_region_init (&textPR, GIMP_DRAWABLE (layer)->tiles,
  764.              0, 0,
  765.              GIMP_DRAWABLE (layer)->width,
  766.              GIMP_DRAWABLE (layer)->height, TRUE);
  767.       pixel_region_init (&maskPR, newmask,
  768.              0, 0,
  769.              GIMP_DRAWABLE (layer)->width,
  770.              GIMP_DRAWABLE (layer)->height, FALSE);
  771.       apply_mask_to_region (&textPR, &maskPR, OPAQUE_OPACITY);
  772.  
  773.       /*  Start a group undo  */
  774.       undo_push_group_start (gimage, TEXT_UNDO);
  775.  
  776.       /*  Set the layer offsets  */
  777.       GIMP_DRAWABLE (layer)->offset_x = text_x;
  778.       GIMP_DRAWABLE (layer)->offset_y = text_y;
  779.  
  780.       /*  If there is a selection mask clear it--
  781.        *  this might not always be desired, but in general,
  782.        *  it seems like the correct behavior.
  783.        */
  784.       if (! gimage_mask_is_empty (gimage))
  785.     channel_clear (gimage_get_mask (gimage));
  786.  
  787.       /*  If the drawable id is invalid, create a new layer  */
  788.       if (drawable == NULL)
  789.     gimage_add_layer (gimage, layer, -1);
  790.       /*  Otherwise, instantiate the text as the new floating selection */
  791.       else
  792.     floating_sel_attach (layer, drawable);
  793.  
  794.       /*  end the group undo  */
  795.       undo_push_group_end (gimage);
  796.  
  797.       tile_manager_destroy (newmask);
  798.     }
  799.   else 
  800.     {
  801.       if (newmask) 
  802.     {
  803.       g_message ("text_render: could not allocate image");
  804.           tile_manager_destroy (newmask);
  805.     }
  806.       layer = NULL;
  807.     }
  808.  
  809.   /* free the pixmap */
  810.   gdk_pixmap_unref (pixmap);
  811.  
  812.   /* free the gc */
  813.   gdk_gc_destroy (gc);
  814.  
  815.   /* free the font */
  816.   gdk_font_unref (font);
  817.  
  818.   return layer;
  819. }
  820.  
  821. gboolean
  822. text_get_extents (gchar *fontname,
  823.           gchar *text,
  824.           gint  *width,
  825.           gint  *height,
  826.           gint  *ascent,
  827.           gint  *descent)
  828. {
  829.   GdkFont *font;
  830.   gchar *str;
  831.   gint   nstrs;
  832.   gint   line_width, line_height;
  833. #ifndef GDK_WINDOWING_WIN32
  834.   XFontStruct *xfs;
  835. #endif
  836.  
  837.   /* load the font in */
  838.   gdk_error_warnings = 0;
  839.   gdk_error_code = 0;
  840. #ifndef GDK_WINDOWING_WIN32
  841.   font = gdk_font_load (fontname);
  842.   if (!font)
  843.     return FALSE;
  844.  
  845.   xfs = GDK_FONT_XFONT (font);
  846.   if (xfs->min_byte1 != 0 || xfs->max_byte1 != 0) 
  847.     {
  848.       gchar *fname;
  849.  
  850.       gdk_font_unref (font);
  851.       fname = g_strdup_printf ("%s,*", fontname);
  852.       font = gdk_fontset_load (fname);
  853.       g_free (fname);
  854.     }
  855. #else
  856.   /* Just use gdk_fontset_load all the time. IMHO it could be like
  857.    * this on all platforms?
  858.    */
  859.   font = gdk_fontset_load (fontname);
  860. #endif
  861.   gdk_error_warnings = 1;
  862.   if (!font || (gdk_error_code == -1))
  863.     return FALSE;
  864.  
  865.   /* determine the bounding box of the text */
  866.   *width = -1;
  867.   *height = 0;
  868.   *ascent = font->ascent;
  869.   *descent = font->descent;
  870.   line_height = *ascent + *descent;
  871.  
  872.   nstrs = 0;
  873.   str = strtok (text, "\n");
  874.   while (str)
  875.     {
  876.       nstrs += 1;
  877.  
  878.       /* gdk_string_measure will give the correct width of the
  879.        *  string. However, we'll add a little "fudge" factor just
  880.        *  to be sure.
  881.        */
  882.       line_width = gdk_string_measure (font, str) + 5;
  883.       if (line_width > *width)
  884.     *width = line_width;
  885.       *height += line_height;
  886.  
  887.       str = strtok (NULL, "\n");
  888.     }
  889.  
  890.   if (*width < 0)
  891.     return FALSE;
  892.   else
  893.     return TRUE;
  894. }
  895.  
  896. static void
  897. text_field_edges (gchar  *fontname,
  898.           gint    field_num,
  899.           /* RETURNS: */
  900.           gchar **start,
  901.           gchar **end)
  902. {
  903.   gchar *t1, *t2;
  904.  
  905.   t1 = fontname;
  906.  
  907.   while (*t1 && (field_num >= 0))
  908.     if (*t1++ == '-')
  909.       field_num--;
  910.  
  911.   t2 = t1;
  912.   while (*t2 && (*t2 != '-'))
  913.     t2++;
  914.  
  915.   *start = t1;
  916.   *end   = t2;
  917. }
  918.  
  919. /* convert sizes back to text */
  920. #define TO_TXT(x) \
  921. { \
  922.   if (x >= 0) \
  923.     g_snprintf (new_ ## x, sizeof (new_ ## x), "%d", x); \
  924.   else \
  925.     g_snprintf (new_ ## x, sizeof (new_ ## x), "*"); \
  926. }
  927.  
  928. /* Multiply the point and pixel sizes in *fontname by "mul", which
  929.  * must be positive.  If either point or pixel sizes are "*" then they
  930.  * are left untouched.  The memory *fontname is g_free()d, and
  931.  * *fontname is replaced by a fresh allocation of the correct size.
  932.  */
  933. static void
  934. text_size_multiply (gchar **fontname,
  935.             gint    mul)
  936. {
  937.   gchar *pixel_str;
  938.   gchar *point_str;
  939.   gchar *newfont;
  940.   gchar *end;
  941.   gint pixel = -1;
  942.   gint point = -1;
  943.   gchar new_pixel[16];
  944.   gchar new_point[16];
  945.  
  946.   /* slice the font spec around the size fields */
  947.   text_field_edges (*fontname, PIXEL_SIZE, &pixel_str, &end);
  948.   text_field_edges (*fontname, POINT_SIZE, &point_str, &end);
  949.  
  950.   *(pixel_str - 1) = 0;
  951.   *(point_str - 1) = 0;
  952.  
  953.   if (*pixel_str != '*')
  954.     pixel = atoi (pixel_str);
  955.  
  956.   if (*point_str != '*')
  957.     point = atoi (point_str);
  958.  
  959.   pixel *= mul;
  960.   point *= mul;
  961.  
  962.   /* convert the pixel and point sizes back to text */
  963.   TO_TXT (pixel);
  964.   TO_TXT (point);
  965.  
  966.   newfont = g_strdup_printf ("%s-%s-%s%s", *fontname, new_pixel, new_point, end);
  967.  
  968.   g_free (*fontname);
  969.  
  970.   *fontname = newfont;
  971. }
  972.  
  973. static void
  974. text_set_resolution (gchar   **fontname,
  975.              gdouble   xresolution,
  976.              gdouble   yresolution)
  977. {
  978.   gchar *size_str;
  979.   gchar *xres_str;
  980.   gchar *yres_str;
  981.   gchar *newfont;
  982.   gchar *end;
  983.   gchar new_size[16];
  984.   gchar new_xres[16];
  985.   gchar new_yres[16];
  986.   gdouble points;
  987.  
  988.   gint size;
  989.   gint xres;
  990.   gint yres;
  991.  
  992.   /* get the point size string */
  993.   text_field_edges (*fontname, POINT_SIZE, &size_str, &end);
  994.  
  995.   /* don't set the resolution if the point size is unspecified */
  996.   if (xresolution < GIMP_MIN_RESOLUTION ||
  997.       yresolution < GIMP_MIN_RESOLUTION ||
  998.       *size_str == '*')
  999.     return;
  1000.  
  1001.   points = atof (size_str);
  1002.  
  1003.   /*  X allows only integer resolution values, so we do some
  1004.    *  ugly calculations (starting with yres because the size of
  1005.    *  a font is it's height)
  1006.    */
  1007.   if (yresolution < 1.0)
  1008.     {
  1009.       points /= (1.0 / yresolution);
  1010.       xresolution *= (1.0 / yresolution);
  1011.       yresolution = 1.0;
  1012.     }
  1013.  
  1014.   /*  res may be != (int) res
  1015.    *  (important only for very small resolutions)
  1016.    */
  1017.   points *= yresolution / (double) (int) yresolution;
  1018.   xresolution /= yresolution / (double) (int) yresolution;
  1019.  
  1020.   /* finally, if xres became invalid by the above calculations */
  1021.   xresolution = CLAMP (xresolution, 1.0, GIMP_MAX_RESOLUTION);
  1022.  
  1023.   /* slice the font spec around the resolution fields */
  1024.   text_field_edges (*fontname, XRESOLUTION, &xres_str, &end);
  1025.   text_field_edges (*fontname, YRESOLUTION, &yres_str, &end);
  1026.  
  1027.   *(size_str - 1) = 0;
  1028.   *(xres_str - 1) = 0;
  1029.   *(yres_str - 1) = 0;
  1030.  
  1031.   /* convert the resolutions to text */
  1032.   size = (gint) points;
  1033.   xres = (gint) xresolution;
  1034.   yres = (gint) yresolution;
  1035.  
  1036.   TO_TXT (size);
  1037.   TO_TXT (xres);
  1038.   TO_TXT (yres);
  1039.  
  1040.   newfont = g_strdup_printf ("%s-%s-%s-%s%s",
  1041.                  *fontname, new_size, new_xres, new_yres, end);
  1042.  
  1043.   g_free (*fontname);
  1044.  
  1045.   *fontname = newfont;
  1046. }
  1047.  
  1048. #undef TO_TXT
  1049.